Skip to content

Commit ea96f1b

Browse files
jintukumardasaniketdivekar
authored andcommitted
Add option to merge genesis script to add balance to existing accounts
1 parent 2f9b176 commit ea96f1b

1 file changed

Lines changed: 137 additions & 56 deletions

File tree

scripts/merge_genesis.py

Lines changed: 137 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
#!/usr/bin/env python3
22
"""
33
Create split account file from old genesis addresses.
4-
Usage: python merge_genesis.py <addresses.json> <main_genesis_file.json> [--balance-multiplier N] [--network NAME]
4+
Usage: python merge_genesis.py <addresses.json> <main_genesis_file.json> [OPTIONS]
55
66
This script creates a new split account file that will be automatically
77
merged when the network starts, instead of modifying the main genesis file.
88
99
Options:
1010
--balance-multiplier N Multiply all balances by N (default: 1)
1111
--network NAME Network name for split files (overrides auto-detection)
12+
--add-balance Add balance to existing addresses in split files instead of skipping duplicates
1213
"""
1314

1415
import json
@@ -176,9 +177,14 @@ def find_next_split_number(genesis_dir: str, base_name: str) -> int:
176177

177178
return max(numbers) + 1 if numbers else 1
178179

179-
def get_existing_addresses_from_all_sources(genesis_dir: str, base_name: str, main_genesis: Dict) -> tuple[Set[str], Set[str]]:
180-
"""Get existing addresses from main genesis and all split files"""
180+
def get_existing_addresses_from_all_sources(genesis_dir: str, base_name: str, main_genesis: Dict) -> tuple[Set[str], Set[str], Dict[str, str]]:
181+
"""Get existing addresses from main genesis and all split files
182+
183+
Returns:
184+
tuple: (existing_eth_addresses, existing_cosmos_addresses, cosmos_to_split_file_map)
185+
"""
181186
existing_eth_addresses, existing_cosmos_addresses = get_existing_addresses(main_genesis)
187+
cosmos_to_split_file_map = {} # Maps cosmos address -> split file path
182188

183189
# Also check split files for existing addresses
184190
pattern = os.path.join(genesis_dir, f"{base_name}.genesis.accounts.*.json")
@@ -194,18 +200,65 @@ def get_existing_addresses_from_all_sources(genesis_dir: str, base_name: str, ma
194200
cosmos_addr = account.get('address', '')
195201
if cosmos_addr and cosmos_addr.startswith('shardeum'):
196202
existing_cosmos_addresses.add(cosmos_addr)
203+
cosmos_to_split_file_map[cosmos_addr] = split_file
197204

198205
# Check balances for cosmos addresses
199206
for balance in split_data.get('balances', []):
200207
cosmos_addr = balance.get('address', '')
201208
if cosmos_addr and cosmos_addr.startswith('shardeum'):
202209
existing_cosmos_addresses.add(cosmos_addr)
210+
cosmos_to_split_file_map[cosmos_addr] = split_file
203211

204212
except Exception as e:
205213
print(f"Warning: Could not read split file {split_file}: {e}", file=sys.stderr)
206214

207215
print(f"Found {len(existing_eth_addresses)} existing Ethereum addresses and {len(existing_cosmos_addresses)} existing Cosmos addresses")
208-
return existing_eth_addresses, existing_cosmos_addresses
216+
return existing_eth_addresses, existing_cosmos_addresses, cosmos_to_split_file_map
217+
218+
def update_balance_in_split_file(split_file_path: str, cosmos_address: str, balance_to_add: int) -> bool:
219+
"""Update balance in an existing split file by adding to existing balance
220+
221+
Args:
222+
split_file_path: Path to the split file
223+
cosmos_address: Cosmos address to update
224+
balance_to_add: Amount to add to existing balance
225+
226+
Returns:
227+
bool: True if successful, False otherwise
228+
"""
229+
try:
230+
# Load the split file
231+
with open(split_file_path, 'r') as f:
232+
split_data = json.load(f)
233+
234+
# Find and update the balance
235+
balance_updated = False
236+
for balance_entry in split_data.get('balances', []):
237+
if balance_entry.get('address') == cosmos_address:
238+
for coin in balance_entry.get('coins', []):
239+
if coin.get('denom') == 'ashm':
240+
old_amount = int(coin.get('amount', '0'))
241+
new_amount = old_amount + balance_to_add
242+
coin['amount'] = str(new_amount)
243+
print(f"Updated balance in {split_file_path}: {cosmos_address} from {old_amount:,} to {new_amount:,} ashm (+{balance_to_add:,})")
244+
balance_updated = True
245+
break
246+
if balance_updated:
247+
break
248+
249+
if not balance_updated:
250+
print(f"Warning: Could not find balance entry for {cosmos_address} in {split_file_path}", file=sys.stderr)
251+
return False
252+
253+
# Save the updated split file
254+
with open(split_file_path, 'w') as f:
255+
json.dump(split_data, f, indent=2)
256+
257+
return True
258+
259+
except Exception as e:
260+
print(f"Error updating balance in split file {split_file_path}: {e}", file=sys.stderr)
261+
return False
209262

210263
def update_main_genesis_supply(main_genesis_path: str, added_balance: int) -> bool:
211264
"""Update the total supply in the main genesis file"""
@@ -246,7 +299,7 @@ def update_main_genesis_supply(main_genesis_path: str, added_balance: int) -> bo
246299
print(f"Error updating main genesis supply: {e}", file=sys.stderr)
247300
return False
248301

249-
def merge_genesis_files(old_genesis_path: str, new_genesis_path: str, balance_multiplier: int = 1, network: str = None):
302+
def merge_genesis_files(old_genesis_path: str, new_genesis_path: str, balance_multiplier: int = 1, network: str = None, add_balance: bool = False):
250303
"""Create split account file from old genesis addresses"""
251304
print(f"Loading old genesis from {old_genesis_path}")
252305
old_genesis = load_old_genesis(old_genesis_path)
@@ -255,6 +308,10 @@ def merge_genesis_files(old_genesis_path: str, new_genesis_path: str, balance_mu
255308
main_genesis = load_new_genesis(new_genesis_path)
256309

257310
print(f"Using balance multiplier: {balance_multiplier}")
311+
if add_balance:
312+
print(f"Add balance mode: ENABLED (will add to existing balances in split files)")
313+
else:
314+
print(f"Add balance mode: DISABLED (will skip duplicate addresses)")
258315

259316
# Determine base name and directory for split files
260317
genesis_path = Path(new_genesis_path)
@@ -270,40 +327,30 @@ def merge_genesis_files(old_genesis_path: str, new_genesis_path: str, balance_mu
270327
print(f"Auto-detected base name: {base_name}")
271328

272329
# Get existing addresses from ALL sources (main genesis + split files)
273-
existing_eth_addresses, existing_cosmos_addresses = get_existing_addresses_from_all_sources(genesis_dir, base_name, main_genesis)
330+
existing_eth_addresses, existing_cosmos_addresses, cosmos_to_split_file_map = get_existing_addresses_from_all_sources(genesis_dir, base_name, main_genesis)
274331

275332
# Prepare data for new split file
276333
new_accounts = []
277334
new_balances = []
278335
added_count = 0
279336
skipped_count = 0
337+
updated_count = 0
280338
total_added_balance = 0
281-
339+
282340
print(f"Processing {len(old_genesis)} addresses from old genesis")
283-
341+
284342
for eth_address, balance_data in old_genesis.items():
285343
# Normalize ethereum address
286344
eth_addr_normalized = eth_address.lower()
287-
345+
288346
# Convert to cosmos address first to check for duplicates
289347
cosmos_address = eth_to_cosmos_address(eth_address)
290348
if not cosmos_address:
291349
print(f"Failed to convert address: {eth_address}")
292350
skipped_count += 1
293351
continue
294-
295-
# Check if either ethereum or cosmos address already exists
296-
if eth_addr_normalized in existing_eth_addresses:
297-
print(f"Skipping duplicate Ethereum address: {eth_address}")
298-
skipped_count += 1
299-
continue
300-
301-
if cosmos_address in existing_cosmos_addresses:
302-
print(f"Skipping duplicate Cosmos address: {cosmos_address} (from Ethereum {eth_address})")
303-
skipped_count += 1
304-
continue
305-
306-
# Get balance in wei
352+
353+
# Get balance in wei early (needed for both adding and updating)
307354
balance_wei = balance_data.get('wei', '0')
308355
balance_int = int(balance_wei)
309356

@@ -317,6 +364,33 @@ def merge_genesis_files(old_genesis_path: str, new_genesis_path: str, balance_mu
317364
skipped_count += 1
318365
continue
319366

367+
# Check if either ethereum or cosmos address already exists
368+
if eth_addr_normalized in existing_eth_addresses:
369+
print(f"Skipping duplicate Ethereum address in main genesis: {eth_address}")
370+
skipped_count += 1
371+
continue
372+
373+
if cosmos_address in existing_cosmos_addresses:
374+
# Address exists - check if we should add to existing balance or skip
375+
if add_balance and cosmos_address in cosmos_to_split_file_map:
376+
# Add balance mode: update the existing balance in the split file
377+
split_file = cosmos_to_split_file_map[cosmos_address]
378+
if update_balance_in_split_file(split_file, cosmos_address, balance_int):
379+
total_added_balance += balance_int
380+
updated_count += 1
381+
else:
382+
print(f"Failed to update balance for {cosmos_address} (from Ethereum {eth_address})")
383+
skipped_count += 1
384+
continue
385+
else:
386+
# Normal mode: skip duplicates
387+
if cosmos_address in cosmos_to_split_file_map:
388+
print(f"Skipping duplicate Cosmos address in split file: {cosmos_address} (from Ethereum {eth_address})")
389+
else:
390+
print(f"Skipping duplicate Cosmos address in main genesis: {cosmos_address} (from Ethereum {eth_address})")
391+
skipped_count += 1
392+
continue
393+
320394
# Create auth account and bank balance for the split file
321395
auth_account = create_auth_account(cosmos_address)
322396
bank_balance = create_bank_balance(cosmos_address, balance_wei)
@@ -337,50 +411,55 @@ def merge_genesis_files(old_genesis_path: str, new_genesis_path: str, balance_mu
337411
else:
338412
print(f"Will add address: {eth_address} -> {cosmos_address} (balance: {balance_wei} wei)")
339413

340-
if added_count == 0:
341-
print("No new addresses to add. No split file created.")
342-
return
343-
344-
# Sort accounts and balances for consistency
345-
new_accounts.sort(key=lambda x: x['address'])
346-
new_balances.sort(key=lambda x: x['address'])
347-
348-
# Create split file data
349-
split_data = {
350-
'accounts': new_accounts,
351-
'balances': new_balances
352-
}
353-
354-
# Find next split file number and create the file
355-
next_number = find_next_split_number(genesis_dir, base_name)
356-
split_file_path = os.path.join(genesis_dir, f"{base_name}.genesis.accounts.{next_number}.json")
357-
358-
print(f"\nCreating split file:")
359-
print(f" Added: {added_count} addresses")
414+
# Print summary
415+
print(f"\nProcessing summary:")
416+
print(f" New addresses added: {added_count}")
417+
print(f" Existing addresses updated: {updated_count}")
360418
print(f" Skipped: {skipped_count} addresses (duplicates or conversion errors)")
361419
if balance_multiplier != 1:
362420
print(f" Balance multiplier: {balance_multiplier}")
363-
print(f" Total added balance: {total_added_balance:,} ashm")
421+
print(f" Total balance added to genesis: {total_added_balance:,} ashm")
364422

365-
# Save split file
366-
try:
367-
with open(split_file_path, 'w') as f:
368-
json.dump(split_data, f, indent=2)
369-
print(f"Successfully created split file: {split_file_path}")
370-
except Exception as e:
371-
print(f"Error creating split file: {e}", file=sys.stderr)
372-
sys.exit(1)
423+
# Create new split file only if we have new addresses
424+
if added_count > 0:
425+
# Sort accounts and balances for consistency
426+
new_accounts.sort(key=lambda x: x['address'])
427+
new_balances.sort(key=lambda x: x['address'])
428+
429+
# Create split file data
430+
split_data = {
431+
'accounts': new_accounts,
432+
'balances': new_balances
433+
}
434+
435+
# Find next split file number and create the file
436+
next_number = find_next_split_number(genesis_dir, base_name)
437+
split_file_path = os.path.join(genesis_dir, f"{base_name}.genesis.accounts.{next_number}.json")
438+
439+
# Save split file
440+
try:
441+
with open(split_file_path, 'w') as f:
442+
json.dump(split_data, f, indent=2)
443+
print(f"\nSuccessfully created new split file: {split_file_path}")
444+
except Exception as e:
445+
print(f"Error creating split file: {e}", file=sys.stderr)
446+
sys.exit(1)
447+
else:
448+
print(f"\nNo new addresses to add. No new split file created.")
373449

374-
# Update main genesis supply
450+
# Update main genesis supply if we added any balance (from new or updated addresses)
375451
if total_added_balance > 0:
376-
print(f"Updating main genesis supply...")
452+
print(f"\nUpdating main genesis supply...")
377453
if not update_main_genesis_supply(new_genesis_path, total_added_balance):
378454
print(f"Warning: Failed to update supply in main genesis", file=sys.stderr)
379455
else:
380456
print(f"Successfully updated supply in main genesis")
381457

382-
print(f"\nSplit file creation complete!")
383-
print(f"The new accounts will be automatically included when the network starts.")
458+
print(f"\nOperation complete!")
459+
if added_count > 0:
460+
print(f"The new accounts will be automatically included when the network starts.")
461+
if updated_count > 0:
462+
print(f"Updated balances have been added to existing accounts in split files.")
384463

385464
def main():
386465
parser = argparse.ArgumentParser(description="Create split account file from old genesis addresses")
@@ -390,10 +469,12 @@ def main():
390469
help="Multiplier to apply to all balances (default: 1)")
391470
parser.add_argument("--network", type=str, default=None,
392471
help="Network name to use for split files (overrides auto-detection)")
472+
parser.add_argument("--add-balance", action="store_true",
473+
help="Add balance to existing addresses in split files instead of skipping duplicates")
393474

394475
args = parser.parse_args()
395476

396-
merge_genesis_files(args.old_genesis, args.main_genesis, args.balance_multiplier, args.network)
477+
merge_genesis_files(args.old_genesis, args.main_genesis, args.balance_multiplier, args.network, args.add_balance)
397478

398479
if __name__ == "__main__":
399480
main()

0 commit comments

Comments
 (0)