Skip to content

Commit 7138fc3

Browse files
[chore] Script for generating CSVs for batch renewal process (#135)
1 parent 7b374f5 commit 7138fc3

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Script to generate a CSV file with token ids and 5-year duration for premint names.
4+
5+
# Default 5-year duration
6+
python3 generate_batch_renewals.py premint1
7+
8+
# Custom 1-year duration
9+
python3 generate_batch_renewals.py premint1 -d 1
10+
11+
# Custom 2.5-year duration with custom output
12+
python3 generate_batch_renewals.py premint1 -o premint1_batch.csv --duration 2.5
13+
14+
# Show help
15+
python3 generate_batch_renewals.py --help
16+
"""
17+
18+
import csv
19+
import argparse
20+
from pathlib import Path
21+
from Crypto.Hash import keccak
22+
23+
def keccak256_to_uint(data: str) -> int:
24+
"""
25+
Calculate keccak256 hash of a string and return as uint.
26+
27+
Args:
28+
data: String to hash
29+
30+
Returns:
31+
Integer representation of the keccak256 hash
32+
"""
33+
# Convert string to bytes and calculate keccak256 hash
34+
k = keccak.new(digest_bits=256)
35+
k.update(data.encode('utf-8'))
36+
37+
# Convert hash bytes to integer
38+
return int.from_bytes(k.digest(), byteorder='big')
39+
40+
def calculate_duration_in_seconds(years: float) -> int:
41+
"""
42+
Calculate duration in seconds using 365.25 days per year.
43+
44+
Args:
45+
years: Number of years
46+
47+
Returns:
48+
Duration in seconds
49+
"""
50+
days_per_year = 365.25
51+
seconds_per_day = 24 * 60 * 60 # 86400 seconds
52+
53+
return int(days_per_year * years * seconds_per_day)
54+
55+
def parse_arguments():
56+
"""Parse command line arguments."""
57+
parser = argparse.ArgumentParser(
58+
description="Generate CSV file with token ids and duration for premint names"
59+
)
60+
parser.add_argument(
61+
"input_file",
62+
help="Path to the input file containing names (one per line)"
63+
)
64+
parser.add_argument(
65+
"-o", "--output",
66+
default="premint_hashes.csv",
67+
help="Output CSV file path (default: premint_hashes.csv)"
68+
)
69+
parser.add_argument(
70+
"-d", "--duration",
71+
type=float,
72+
default=5.0,
73+
help="Duration in years (default: 5.0)"
74+
)
75+
return parser.parse_args()
76+
77+
def load_excluded_names(script_dir: Path) -> set:
78+
"""Load names to exclude from the lostnames file."""
79+
lostnames_file = script_dir / "lostnames"
80+
excluded_names = set()
81+
82+
if lostnames_file.exists():
83+
with open(lostnames_file, 'r') as f:
84+
for line in f:
85+
name = line.strip()
86+
if name:
87+
excluded_names.add(name.lower()) # Store in lowercase for case-insensitive comparison
88+
89+
return excluded_names
90+
91+
def main():
92+
"""Main function to generate the CSV file."""
93+
94+
# Parse command line arguments
95+
args = parse_arguments()
96+
97+
# Path to the input and output files
98+
input_file = Path(args.input_file)
99+
output_file = Path(args.output)
100+
script_dir = Path(__file__).parent
101+
102+
# Check if input file exists
103+
if not input_file.exists():
104+
print(f"Error: Input file '{input_file}' not found.")
105+
return 1
106+
107+
# Load excluded names from lostnames file
108+
excluded_names = load_excluded_names(script_dir)
109+
print(f"Loaded {len(excluded_names)} excluded names from lostnames file")
110+
111+
# Calculate duration in seconds
112+
duration_seconds = calculate_duration_in_seconds(args.duration)
113+
print(f"Duration: {args.duration} years ({duration_seconds} seconds)")
114+
print(f"Input file: {input_file}")
115+
print(f"Output file: {output_file}")
116+
117+
# Read names from input file and collect unique IDs
118+
seen_ids = set()
119+
unique_entries = []
120+
duplicate_names = []
121+
excluded_names_found = []
122+
123+
with open(input_file, 'r') as infile:
124+
for line_num, line in enumerate(infile, 1):
125+
name = line.strip()
126+
if name: # Skip empty lines
127+
# Check if name is in excluded list (case-insensitive)
128+
if name.lower() in excluded_names:
129+
excluded_names_found.append((line_num, name))
130+
continue
131+
132+
name_id = keccak256_to_uint(name)
133+
if name_id not in seen_ids:
134+
seen_ids.add(name_id)
135+
unique_entries.append((name_id, duration_seconds, name))
136+
else:
137+
duplicate_names.append((line_num, name))
138+
139+
# Write CSV with unique entries only
140+
with open(output_file, 'w', newline='') as outfile:
141+
csv_writer = csv.writer(outfile)
142+
143+
# Write header
144+
csv_writer.writerow(['id', 'duration'])
145+
146+
# Write unique entries
147+
for name_id, duration, _ in unique_entries:
148+
csv_writer.writerow([name_id, duration])
149+
150+
# Report results
151+
total_names = sum(1 for line in open(input_file) if line.strip())
152+
unique_count = len(unique_entries)
153+
duplicate_count = len(duplicate_names)
154+
excluded_count = len(excluded_names_found)
155+
156+
print(f"CSV file generated: {output_file}")
157+
print(f"Total names processed: {total_names}")
158+
print(f"Unique entries written: {unique_count}")
159+
print(f"Duplicates found and skipped: {duplicate_count}")
160+
print(f"Excluded names found and skipped: {excluded_count}")
161+
162+
if duplicate_names:
163+
print("\nDuplicate names found:")
164+
for line_num, name in duplicate_names:
165+
print(f" Line {line_num}: {name}")
166+
else:
167+
print("No duplicates found.")
168+
169+
if excluded_names_found:
170+
print("\nExcluded names found (from lostnames):")
171+
for line_num, name in excluded_names_found:
172+
print(f" Line {line_num}: {name}")
173+
else:
174+
print("No excluded names found.")
175+
return 0
176+
177+
if __name__ == "__main__":
178+
exit_code = main()
179+
exit(exit_code)

0 commit comments

Comments
 (0)