|
| 1 | +#!/usr/bin/env python |
| 2 | +""" |
| 3 | +Usage: update_checkpoints.py [-h] [-n NETWORK] |
| 4 | +
|
| 5 | +Helper script to update the config checkpoint list. |
| 6 | +
|
| 7 | +options: |
| 8 | + -h, --help show this help message and exit |
| 9 | + -n NETWORK, --network NETWORK |
| 10 | + The network to update (default: mainnet) |
| 11 | +
|
| 12 | +For example: |
| 13 | +
|
| 14 | +$ ./extras/update_checkpoints.py |
| 15 | +New checkpoints to add for mainnet: |
| 16 | +
|
| 17 | + 4_800_000: 00000000000000000716b8d9e96591ba7cb2d02c3d2d1d98d514f41c240fdff7 |
| 18 | + 4_900_000: 0000000000000000079b1c1ebf48d351a7d31dcc55c5b4cf79ade79089a20f5a |
| 19 | + 5_000_000: 000000000000000006c9167db1cc7e93fcf1c3014da6c6221390d03d1640c9b3 |
| 20 | +
|
| 21 | + cp(4_800_000, bytes.fromhex('00000000000000000716b8d9e96591ba7cb2d02c3d2d1d98d514f41c240fdff7')), |
| 22 | + cp(4_900_000, bytes.fromhex('0000000000000000079b1c1ebf48d351a7d31dcc55c5b4cf79ade79089a20f5a')), |
| 23 | + cp(5_000_000, bytes.fromhex('000000000000000006c9167db1cc7e93fcf1c3014da6c6221390d03d1640c9b3')), |
| 24 | +
|
| 25 | +The output can then be copied and pasted into `hathor/conf/mainnet.yml` and `hathor/conf/mainnet.py` |
| 26 | +""" |
| 27 | + |
| 28 | +import requests |
| 29 | +import yaml |
| 30 | +import argparse |
| 31 | + |
| 32 | +# Built-in network configurations |
| 33 | +NETWORKS: dict[str, dict[str, str]] = { |
| 34 | + 'mainnet': { |
| 35 | + 'config_file': 'hathor/conf/mainnet.yml', |
| 36 | + 'node_url': 'https://node1.mainnet.hathor.network/v1a', |
| 37 | + }, |
| 38 | + 'testnet': { |
| 39 | + 'config_file': 'hathor/conf/testnet.yml', |
| 40 | + 'node_url': 'https://node1.golf.testnet.hathor.network/v1a', |
| 41 | + }, |
| 42 | + # Add more networks as needed |
| 43 | +} |
| 44 | + |
| 45 | +CHECKPOINT_INTERVAL: int = 100_000 |
| 46 | + |
| 47 | + |
| 48 | +def get_latest_height(node_url: str) -> int: |
| 49 | + """Fetch the latest block height.""" |
| 50 | + response = requests.get(f'{node_url}/transaction?type=block&count=1') |
| 51 | + response.raise_for_status() |
| 52 | + return response.json()['transactions'][0]['height'] |
| 53 | + |
| 54 | + |
| 55 | +def get_hash_for_height(node_url: str, height: int) -> str: |
| 56 | + """Fetch the hash for a given block height.""" |
| 57 | + response = requests.get(f'{node_url}/block_at_height?height={height}') |
| 58 | + response.raise_for_status() |
| 59 | + return response.json()['block']['tx_id'] |
| 60 | + |
| 61 | + |
| 62 | +def load_checkpoints(config_file: str) -> dict[str, int]: |
| 63 | + """Load the checkpoints from the specified YAML config file.""" |
| 64 | + with open(config_file, 'r') as file: |
| 65 | + data = yaml.safe_load(file) |
| 66 | + return data.get('CHECKPOINTS', {}) |
| 67 | + |
| 68 | + |
| 69 | +def print_new_checkpoints(network_name: str) -> None: |
| 70 | + """Print new checkpoints for the specified network.""" |
| 71 | + if network_name not in NETWORKS: |
| 72 | + print(f'Error: Unknown network {network_name}. Available networks: {", ".join(NETWORKS.keys())}') |
| 73 | + return |
| 74 | + |
| 75 | + # Get the network configuration |
| 76 | + network_config = NETWORKS[network_name] |
| 77 | + config_file = network_config['config_file'] |
| 78 | + node_url = network_config['node_url'] |
| 79 | + |
| 80 | + # Load existing checkpoints from the YAML file |
| 81 | + current_checkpoints = load_checkpoints(config_file) |
| 82 | + |
| 83 | + # Get the latest block height |
| 84 | + latest_height = get_latest_height(node_url) |
| 85 | + |
| 86 | + # Determine missing checkpoints |
| 87 | + new_checkpoints = {} |
| 88 | + for height in range(CHECKPOINT_INTERVAL, latest_height + 1, CHECKPOINT_INTERVAL): |
| 89 | + if height not in current_checkpoints: |
| 90 | + block_hash = get_hash_for_height(node_url, height) |
| 91 | + new_checkpoints[height] = block_hash |
| 92 | + |
| 93 | + # Print new checkpoints |
| 94 | + if new_checkpoints: |
| 95 | + print(f'New checkpoints to add for {network_name}:\n') |
| 96 | + for height, block_hash in sorted(new_checkpoints.items()): |
| 97 | + print(f' {height:_}: {block_hash}') |
| 98 | + print() |
| 99 | + for height, block_hash in sorted(new_checkpoints.items()): |
| 100 | + print(f''' cp({height:_}, bytes.fromhex('{block_hash}')),''') |
| 101 | + else: |
| 102 | + print(f'No new checkpoints needed for {network_name}. All up to date.') |
| 103 | + |
| 104 | + |
| 105 | +if __name__ == '__main__': |
| 106 | + # Parse command-line arguments |
| 107 | + parser = argparse.ArgumentParser(description='Helper script to update the config checkpoint list.') |
| 108 | + parser.add_argument('-n', '--network', default='mainnet', help='The network to update (default: mainnet)') |
| 109 | + args = parser.parse_args() |
| 110 | + |
| 111 | + # Print new checkpoints for the specified network |
| 112 | + print_new_checkpoints(args.network) |
0 commit comments