diff --git a/cli/genesis_cli.py b/cli/genesis_cli.py index 3ed98900..9b4577aa 100755 --- a/cli/genesis_cli.py +++ b/cli/genesis_cli.py @@ -13,25 +13,43 @@ from pathlib import Path def handle_genesis_init(args): """Initialize genesis block and wallet""" - script_path = Path("/opt/aitbc/apps/blockchain-node/scripts/unified_genesis.py") + # Use new genesis-init.py script for basic genesis initialization + new_script_path = Path("/opt/aitbc/scripts/utils/genesis-init.py") + old_script_path = Path("/opt/aitbc/apps/blockchain-node/scripts/unified_genesis.py") - if not script_path.exists(): - print(f"Error: Genesis generation script not found: {script_path}") + # Prefer new script if it exists and we're not doing wallet creation + if new_script_path.exists() and not args.create_wallet: + script_path = new_script_path + use_new_script = True + elif old_script_path.exists(): + script_path = old_script_path + use_new_script = False + else: + print(f"Error: Genesis generation script not found") return 1 - cmd = [sys.executable, str(script_path), "--chain-id", args.chain_id] - - if args.create_wallet: - cmd.append("--create-wallet") - if args.password: - cmd.extend(["--password", args.password]) - if args.proposer: - cmd.extend(["--proposer", args.proposer]) - if args.force: - cmd.append("--force") - if args.register_service: - cmd.append("--register-service") - cmd.extend(["--service-url", args.service_url]) + if use_new_script: + # Use new simpler script + cmd = [sys.executable, str(script_path), "--chain-id", args.chain_id] + if args.proposer: + cmd.extend(["--proposer", args.proposer]) + else: + print("Error: --proposer is required for genesis initialization") + return 1 + else: + # Use old comprehensive script for wallet creation + cmd = [sys.executable, str(script_path), "--chain-id", args.chain_id] + if args.create_wallet: + cmd.append("--create-wallet") + if args.password: + cmd.extend(["--password", args.password]) + if args.proposer: + cmd.extend(["--proposer", args.proposer]) + if args.force: + cmd.append("--force") + if args.register_service: + cmd.append("--register-service") + cmd.extend(["--service-url", args.service_url]) try: result = subprocess.run(cmd, capture_output=True, text=True, check=True) diff --git a/cli/unified_cli.py b/cli/unified_cli.py index eb1e3f80..d4aaade2 100755 --- a/cli/unified_cli.py +++ b/cli/unified_cli.py @@ -645,25 +645,43 @@ def handle_genesis_init(args): import sys from pathlib import Path - script_path = Path("/opt/aitbc/apps/blockchain-node/scripts/unified_genesis.py") + # Use new genesis-init.py script for basic genesis initialization + new_script_path = Path("/opt/aitbc/scripts/utils/genesis-init.py") + old_script_path = Path("/opt/aitbc/apps/blockchain-node/scripts/unified_genesis.py") - if not script_path.exists(): - print(f"Error: Genesis generation script not found: {script_path}") + # Prefer new script if it exists and we're not doing wallet creation + if new_script_path.exists() and not args.create_wallet: + script_path = new_script_path + use_new_script = True + elif old_script_path.exists(): + script_path = old_script_path + use_new_script = False + else: + print(f"Error: Genesis generation script not found") return - cmd = [sys.executable, str(script_path), "--chain-id", args.chain_id] - - if args.create_wallet: - cmd.append("--create-wallet") - if args.password: - cmd.extend(["--password", args.password]) - if args.proposer: - cmd.extend(["--proposer", args.proposer]) - if args.force: - cmd.append("--force") - if args.register_service: - cmd.append("--register-service") - cmd.extend(["--service-url", args.service_url]) + if use_new_script: + # Use new simpler script + cmd = [sys.executable, str(script_path), "--chain-id", args.chain_id] + if args.proposer: + cmd.extend(["--proposer", args.proposer]) + else: + print("Error: --proposer is required for genesis initialization") + return + else: + # Use old comprehensive script for wallet creation + cmd = [sys.executable, str(script_path), "--chain-id", args.chain_id] + if args.create_wallet: + cmd.append("--create-wallet") + if args.password: + cmd.extend(["--password", args.password]) + if args.proposer: + cmd.extend(["--proposer", args.proposer]) + if args.force: + cmd.append("--force") + if args.register_service: + cmd.append("--register-service") + cmd.extend(["--service-url", args.service_url]) try: result = subprocess.run(cmd, capture_output=True, text=True, check=True) diff --git a/scripts/utils/genesis-init.py b/scripts/utils/genesis-init.py new file mode 100755 index 00000000..14cebc8e --- /dev/null +++ b/scripts/utils/genesis-init.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python3 +""" +Genesis Block Initialization Script + +This script creates genesis blocks and initializes accounts for AITBC chains. +It can be used to set up new chains or re-initialize existing ones. + +Usage: + python scripts/utils/genesis-init.py --chain-id ait-testnet --proposer
--balance +""" + +import argparse +import json +import sqlite3 +import sys +from datetime import datetime +from pathlib import Path +from typing import Optional, List, Dict, Any + + +def compute_genesis_hash(height: int, parent_hash: str, timestamp: datetime) -> str: + """Compute genesis block hash (simplified - in production use proper crypto)""" + # For now, use a deterministic hash based on the genesis data + data = f"{height}:{parent_hash}:{timestamp.isoformat()}:genesis" + import hashlib + return hashlib.sha256(data.encode()).hexdigest() + + +def create_genesis_json( + chain_id: str, + proposer: str, + balance: int, + output_path: Optional[Path] = None, + chain_type: str = "testnet" +) -> Path: + """Create genesis.json file for a chain""" + + timestamp = datetime(2025, 1, 1, 0, 0, 0) + genesis_hash = compute_genesis_hash(0, "0x00", timestamp) + + genesis_data = { + "chain_id": chain_id, + "block": { + "height": 0, + "hash": genesis_hash, + "parent_hash": "0x00", + "proposer": proposer, + "timestamp": timestamp.isoformat(), + "tx_count": 0, + "chain_id": chain_id, + "state_root": "0x00", + "metadata": { + "chain_type": chain_type, + "purpose": "testing" if chain_type == "testnet" else "production", + "consensus_algorithm": "poa" + } + }, + "allocations": [ + { + "address": proposer, + "balance": balance, + "nonce": 0 + } + ] + } + + if output_path is None: + output_path = Path(f"/var/lib/aitbc/data/{chain_id}/genesis.json") + + output_path.parent.mkdir(parents=True, exist_ok=True) + + with open(output_path, 'w') as f: + json.dump(genesis_data, f, indent=2) + + print(f"Created genesis.json at {output_path}") + return output_path + + +def initialize_database( + chain_id: str, + proposer: str, + balance: int, + db_path: Optional[Path] = None +) -> None: + """Initialize database with genesis block and account""" + + if db_path is None: + db_path = Path(f"/var/lib/aitbc/data/{chain_id}/chain.db") + + db_path.parent.mkdir(parents=True, exist_ok=True) + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + # Create tables (simplified schema - matches SQLModel models) + cursor.execute(""" + CREATE TABLE IF NOT EXISTS block ( + id INTEGER NOT NULL, + chain_id VARCHAR NOT NULL, + height INTEGER NOT NULL, + hash VARCHAR NOT NULL, + parent_hash VARCHAR NOT NULL, + proposer VARCHAR NOT NULL, + timestamp DATETIME NOT NULL, + tx_count INTEGER NOT NULL, + state_root VARCHAR, + block_metadata VARCHAR, + PRIMARY KEY (id), + CONSTRAINT uix_block_chain_height UNIQUE (chain_id, height) + ) + """) + + cursor.execute(""" + CREATE TABLE IF NOT EXISTS account ( + chain_id VARCHAR NOT NULL, + address VARCHAR NOT NULL, + balance INTEGER NOT NULL, + nonce INTEGER NOT NULL, + updated_at DATETIME NOT NULL, + PRIMARY KEY (chain_id, address) + ) + """) + + # Create indexes + cursor.execute("CREATE INDEX IF NOT EXISTS ix_block_chain_id ON block (chain_id)") + cursor.execute("CREATE INDEX IF NOT EXISTS ix_block_height ON block (height)") + cursor.execute("CREATE UNIQUE INDEX IF NOT EXISTS ix_block_hash ON block (hash)") + + # Check if genesis block already exists + cursor.execute( + "SELECT hash FROM block WHERE chain_id=? AND height=0", + (chain_id,) + ) + existing = cursor.fetchone() + + if existing: + print(f"Genesis block already exists for chain {chain_id}") + conn.close() + return + + # Insert genesis block + timestamp = datetime(2025, 1, 1, 0, 0, 0) + genesis_hash = compute_genesis_hash(0, "0x00", timestamp) + + cursor.execute( + """ + INSERT INTO block (chain_id, height, hash, parent_hash, proposer, timestamp, tx_count, state_root) + VALUES (?, 0, ?, ?, ?, ?, 0, ?) + """, + (chain_id, genesis_hash, "0x00", proposer, timestamp.isoformat(), "0x00") + ) + + # Insert account with initial balance + cursor.execute( + """ + INSERT INTO account (chain_id, address, balance, nonce, updated_at) + VALUES (?, ?, ?, ?, ?) + """, + (chain_id, proposer, balance, 0, timestamp.isoformat()) + ) + + conn.commit() + conn.close() + + print(f"Initialized database for chain {chain_id}") + print(f"Genesis block hash: {genesis_hash}") + print(f"Account {proposer} balance: {balance}") + + +def main(): + parser = argparse.ArgumentParser(description="Initialize genesis block for AITBC chain") + parser.add_argument("--chain-id", required=True, help="Chain ID (e.g., ait-testnet)") + parser.add_argument("--proposer", required=True, help="Proposer address for genesis block") + parser.add_argument("--balance", type=int, default=1000000000, help="Initial balance (default: 1,000,000,000)") + parser.add_argument("--chain-type", default="testnet", choices=["mainnet", "testnet"], help="Chain type") + parser.add_argument("--db-path", help="Custom database path") + parser.add_argument("--genesis-path", help="Custom genesis.json path") + parser.add_argument("--skip-genesis", action="store_true", help="Skip creating genesis.json") + parser.add_argument("--skip-db", action="store_true", help="Skip database initialization") + + args = parser.parse_args() + + if not args.skip_genesis: + genesis_path = Path(args.genesis_path) if args.genesis_path else None + create_genesis_json( + chain_id=args.chain_id, + proposer=args.proposer, + balance=args.balance, + output_path=genesis_path, + chain_type=args.chain_type + ) + + if not args.skip_db: + db_path = Path(args.db_path) if args.db_path else None + initialize_database( + chain_id=args.chain_id, + proposer=args.proposer, + balance=args.balance, + db_path=db_path + ) + + print(f"\nGenesis initialization complete for chain {args.chain_id}") + + +if __name__ == "__main__": + main()