add genesis command to CLI
Some checks failed
Blockchain Synchronization Verification / sync-verification (push) Failing after 3s
CLI Tests / test-cli (push) Failing after 3s
Documentation Validation / validate-docs (push) Successful in 5s
Documentation Validation / validate-policies-strict (push) Successful in 3s
Integration Tests / test-service-integration (push) Successful in 1m10s
Multi-Node Blockchain Health Monitoring / health-check (push) Successful in 2s
P2P Network Verification / p2p-verification (push) Successful in 1s
Python Tests / test-python (push) Failing after 10s
Security Scanning / security-scan (push) Successful in 1m10s
Some checks failed
Blockchain Synchronization Verification / sync-verification (push) Failing after 3s
CLI Tests / test-cli (push) Failing after 3s
Documentation Validation / validate-docs (push) Successful in 5s
Documentation Validation / validate-policies-strict (push) Successful in 3s
Integration Tests / test-service-integration (push) Successful in 1m10s
Multi-Node Blockchain Health Monitoring / health-check (push) Successful in 2s
P2P Network Verification / p2p-verification (push) Successful in 1s
Python Tests / test-python (push) Failing after 10s
Security Scanning / security-scan (push) Successful in 1m10s
Imported genesis command module and registered it with the CLI command group.
This commit is contained in:
111
apps/blockchain-node/scripts/create_genesis_wallet.py
Normal file
111
apps/blockchain-node/scripts/create_genesis_wallet.py
Normal file
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Create a new genesis wallet with secure random private key"""
|
||||
|
||||
import json
|
||||
import hashlib
|
||||
from cryptography.hazmat.primitives.asymmetric import ed25519
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from datetime import datetime
|
||||
import secrets
|
||||
import base64
|
||||
import os
|
||||
from pathlib import Path
|
||||
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||
|
||||
def derive_address_from_public_key(pub_key_bytes: bytes) -> str:
|
||||
"""Derive AITBC address from public key"""
|
||||
# Hash the public key
|
||||
digest = hashlib.sha256(pub_key_bytes).digest()
|
||||
# Take first 20 bytes and encode as hex
|
||||
address_hash = digest[:20].hex()
|
||||
# Return with aitbc1 prefix
|
||||
return f"aitbc1{address_hash}"
|
||||
|
||||
def create_genesis_wallet(password: str = None):
|
||||
"""Create genesis wallet with secure random private key"""
|
||||
# Generate cryptographically secure random private key (32 bytes)
|
||||
private_key_bytes = secrets.token_bytes(32)
|
||||
|
||||
# Generate Ed25519 key pair from private key
|
||||
private_key = ed25519.Ed25519PrivateKey.from_private_bytes(private_key_bytes)
|
||||
public_key = private_key.public_key()
|
||||
|
||||
# Get public key bytes
|
||||
pub_key_bytes = public_key.public_bytes(
|
||||
encoding=serialization.Encoding.Raw,
|
||||
format=serialization.PublicFormat.Raw
|
||||
)
|
||||
|
||||
# Derive address
|
||||
address = derive_address_from_public_key(pub_key_bytes)
|
||||
|
||||
# Convert to ait1 prefix format (matching genesis.json format)
|
||||
ait_address = address.replace("aitbc1", "ait1")
|
||||
|
||||
# Generate password if not provided
|
||||
if not password:
|
||||
password = secrets.token_urlsafe(32)
|
||||
|
||||
# Encrypt private key with password
|
||||
salt = secrets.token_bytes(16)
|
||||
kdf = PBKDF2HMAC(
|
||||
algorithm=hashes.SHA256(),
|
||||
length=32,
|
||||
salt=salt,
|
||||
iterations=100000,
|
||||
)
|
||||
key = kdf.derive(password.encode())
|
||||
|
||||
# Encrypt using AES-GCM
|
||||
aesgcm = AESGCM(key)
|
||||
nonce = secrets.token_bytes(12)
|
||||
ciphertext = aesgcm.encrypt(nonce, private_key_bytes, None)
|
||||
|
||||
# Create wallet data
|
||||
wallet_data = {
|
||||
"address": ait_address,
|
||||
"public_key": pub_key_bytes.hex(),
|
||||
"crypto": {
|
||||
"kdf": "pbkdf2",
|
||||
"kdfparams": {
|
||||
"salt": salt.hex(),
|
||||
"c": 100000,
|
||||
"dklen": 32,
|
||||
"prf": "hmac-sha256"
|
||||
},
|
||||
"cipher": "aes-256-gcm",
|
||||
"cipherparams": {
|
||||
"nonce": nonce.hex()
|
||||
},
|
||||
"ciphertext": ciphertext.hex()
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
|
||||
# Write to keystore
|
||||
keystore_path = Path("/var/lib/aitbc/keystore/genesis.json")
|
||||
with open(keystore_path, 'w') as f:
|
||||
json.dump(wallet_data, f, indent=2)
|
||||
|
||||
# Save password to secure file
|
||||
password_path = Path("/var/lib/aitbc/keystore/.genesis_password")
|
||||
with open(password_path, 'w') as f:
|
||||
f.write(password)
|
||||
os.chmod(password_path, 0o600)
|
||||
|
||||
print(f"✅ Created new genesis wallet with secure random private key")
|
||||
print(f"Address: {ait_address}")
|
||||
print(f"Public key: {pub_key_bytes.hex()}")
|
||||
print(f"Private key: {private_key_bytes.hex()}")
|
||||
print(f"Password: {password}")
|
||||
print(f"Wallet saved to: {keystore_path}")
|
||||
print(f"Password saved to: {password_path}")
|
||||
print(f"⚠️ IMPORTANT: Store the password securely!")
|
||||
|
||||
return ait_address, pub_key_bytes.hex(), private_key_bytes.hex(), password
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_genesis_wallet()
|
||||
350
apps/blockchain-node/scripts/unified_genesis.py
Normal file
350
apps/blockchain-node/scripts/unified_genesis.py
Normal file
@@ -0,0 +1,350 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Unified Genesis Block and Wallet Generation
|
||||
|
||||
This script combines genesis block creation with genesis wallet generation,
|
||||
connected to the wallet service for proper key management and storage.
|
||||
|
||||
Usage:
|
||||
python3 unified_genesis.py --chain-id ait-mainnet --create-wallet
|
||||
python3 unified_genesis.py --chain-id ait-mainnet --force
|
||||
"""
|
||||
|
||||
import json
|
||||
import hashlib
|
||||
import argparse
|
||||
import secrets
|
||||
import base64
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Any, Optional
|
||||
|
||||
from cryptography.hazmat.primitives.asymmetric import ed25519
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||
|
||||
# Add project paths
|
||||
sys.path.insert(0, '/opt/aitbc')
|
||||
sys.path.insert(0, '/opt/aitbc/apps/blockchain-node/src')
|
||||
|
||||
try:
|
||||
from aitbc_chain.config import BlockchainConfig
|
||||
from aitbc_chain.models import Block, Account
|
||||
from sqlmodel import Session, create_engine, select
|
||||
except ImportError:
|
||||
print("Warning: Could not import blockchain modules, running in wallet-only mode")
|
||||
|
||||
|
||||
def derive_address_from_public_key(pub_key_bytes: bytes) -> str:
|
||||
"""Derive AITBC address from public key"""
|
||||
digest = hashlib.sha256(pub_key_bytes).digest()
|
||||
address_hash = digest[:20].hex()
|
||||
return f"aitbc1{address_hash}"
|
||||
|
||||
|
||||
def compute_block_hash(height: int, parent_hash: str, timestamp: datetime, chain_id: str = "ait-mainnet") -> str:
|
||||
"""Compute block hash"""
|
||||
hash_input = f"{height}{parent_hash}{timestamp.isoformat()}{chain_id}".encode()
|
||||
return hashlib.sha256(hash_input).hexdigest()
|
||||
|
||||
|
||||
def create_genesis_wallet(password: str = None, chain_id: str = "ait-mainnet") -> Dict[str, str]:
|
||||
"""Create genesis wallet with secure random private key"""
|
||||
# Generate cryptographically secure random private key
|
||||
private_key_bytes = secrets.token_bytes(32)
|
||||
|
||||
# Generate Ed25519 key pair
|
||||
private_key = ed25519.Ed25519PrivateKey.from_private_bytes(private_key_bytes)
|
||||
public_key = private_key.public_key()
|
||||
|
||||
# Get public key bytes
|
||||
pub_key_bytes = public_key.public_bytes(
|
||||
encoding=serialization.Encoding.Raw,
|
||||
format=serialization.PublicFormat.Raw
|
||||
)
|
||||
|
||||
# Derive address
|
||||
address = derive_address_from_public_key(pub_key_bytes)
|
||||
ait_address = address.replace("aitbc1", "ait1")
|
||||
|
||||
# Generate password if not provided
|
||||
if not password:
|
||||
password = secrets.token_urlsafe(32)
|
||||
|
||||
# Encrypt private key with password
|
||||
salt = secrets.token_bytes(16)
|
||||
kdf = PBKDF2HMAC(
|
||||
algorithm=hashes.SHA256(),
|
||||
length=32,
|
||||
salt=salt,
|
||||
iterations=100000,
|
||||
)
|
||||
key = kdf.derive(password.encode())
|
||||
|
||||
# Encrypt using AES-GCM
|
||||
aesgcm = AESGCM(key)
|
||||
nonce = secrets.token_bytes(12)
|
||||
ciphertext = aesgcm.encrypt(nonce, private_key_bytes, None)
|
||||
|
||||
# Create wallet data
|
||||
wallet_data = {
|
||||
"address": ait_address,
|
||||
"public_key": pub_key_bytes.hex(),
|
||||
"crypto": {
|
||||
"kdf": "pbkdf2",
|
||||
"kdfparams": {
|
||||
"salt": salt.hex(),
|
||||
"c": 100000,
|
||||
"dklen": 32,
|
||||
"prf": "hmac-sha256"
|
||||
},
|
||||
"cipher": "aes-256-gcm",
|
||||
"cipherparams": {
|
||||
"nonce": nonce.hex()
|
||||
},
|
||||
"ciphertext": ciphertext.hex()
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
|
||||
return {
|
||||
"wallet": wallet_data,
|
||||
"address": ait_address,
|
||||
"public_key": pub_key_bytes.hex(),
|
||||
"private_key": private_key_bytes.hex(),
|
||||
"password": password
|
||||
}
|
||||
|
||||
|
||||
def create_genesis_block(chain_id: str, proposer: str, timestamp: datetime = None) -> Dict[str, Any]:
|
||||
"""Create genesis block"""
|
||||
if not timestamp:
|
||||
timestamp = datetime.fromisoformat("2025-01-01 00:00:00")
|
||||
|
||||
parent_hash = "0x00"
|
||||
genesis_hash = compute_block_hash(0, parent_hash, timestamp, chain_id)
|
||||
|
||||
genesis_block = {
|
||||
"height": 0,
|
||||
"hash": genesis_hash,
|
||||
"parent_hash": parent_hash,
|
||||
"proposer": proposer,
|
||||
"timestamp": timestamp.isoformat(),
|
||||
"tx_count": 0,
|
||||
"chain_id": chain_id,
|
||||
"state_root": "0x00",
|
||||
"metadata": {
|
||||
"chain_type": "mainnet",
|
||||
"purpose": "production",
|
||||
"consensus_algorithm": "poa"
|
||||
}
|
||||
}
|
||||
|
||||
return genesis_block
|
||||
|
||||
|
||||
def create_genesis_allocations(genesis_address: str, additional_allocations: List[Dict] = None) -> List[Dict]:
|
||||
"""Create genesis allocations including genesis wallet"""
|
||||
allocations = [
|
||||
{
|
||||
"address": genesis_address,
|
||||
"balance": 1000000000, # 1 billion AIT for genesis
|
||||
"nonce": 0
|
||||
}
|
||||
]
|
||||
|
||||
if additional_allocations:
|
||||
allocations.extend(additional_allocations)
|
||||
|
||||
return allocations
|
||||
|
||||
|
||||
def save_genesis_wallet(wallet_data: Dict, keystore_path: Path, password: str):
|
||||
"""Save genesis wallet to keystore"""
|
||||
keystore_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(keystore_path, 'w') as f:
|
||||
json.dump(wallet_data, f, indent=2)
|
||||
|
||||
# Save password securely
|
||||
password_path = keystore_path.parent / ".genesis_password"
|
||||
with open(password_path, 'w') as f:
|
||||
f.write(password)
|
||||
os.chmod(password_path, 0o600)
|
||||
|
||||
|
||||
def save_genesis_json(genesis_block: Dict, allocations: List[Dict], genesis_path: Path):
|
||||
"""Save genesis configuration to JSON file"""
|
||||
genesis_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
genesis_config = {
|
||||
"chain_id": genesis_block["chain_id"],
|
||||
"block": genesis_block,
|
||||
"allocations": allocations
|
||||
}
|
||||
|
||||
with open(genesis_path, 'w') as f:
|
||||
json.dump(genesis_config, f, indent=2)
|
||||
|
||||
|
||||
def initialize_genesis_database(genesis_block: Dict, allocations: List[Dict], db_path: Path):
|
||||
"""Initialize blockchain database with genesis data"""
|
||||
try:
|
||||
engine = create_engine(f"sqlite:///{db_path}")
|
||||
with Session(engine) as session:
|
||||
# Check if genesis already exists
|
||||
existing = session.exec(
|
||||
select(Block).where(Block.height == 0).where(Block.chain_id == genesis_block["chain_id"])
|
||||
).first()
|
||||
|
||||
if existing:
|
||||
print(f"⚠️ Genesis block already exists in database")
|
||||
return False
|
||||
|
||||
# Create genesis block
|
||||
block = Block(
|
||||
height=genesis_block["height"],
|
||||
hash=genesis_block["hash"],
|
||||
parent_hash=genesis_block["parent_hash"],
|
||||
proposer=genesis_block["proposer"],
|
||||
timestamp=datetime.fromisoformat(genesis_block["timestamp"]),
|
||||
tx_count=genesis_block["tx_count"],
|
||||
chain_id=genesis_block["chain_id"],
|
||||
state_root=genesis_block["state_root"]
|
||||
)
|
||||
session.add(block)
|
||||
|
||||
# Create genesis accounts
|
||||
for alloc in allocations:
|
||||
account = Account(
|
||||
chain_id=genesis_block["chain_id"],
|
||||
address=alloc["address"],
|
||||
balance=alloc["balance"],
|
||||
nonce=alloc["nonce"],
|
||||
updated_at=datetime.utcnow()
|
||||
)
|
||||
session.add(account)
|
||||
|
||||
session.commit()
|
||||
print(f"✅ Genesis initialized in database: {db_path}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error initializing genesis in database: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def register_wallet_with_service(wallet_address: str, wallet_data: Dict, service_url: str = "http://localhost:8003"):
|
||||
"""Register genesis wallet with wallet daemon service"""
|
||||
try:
|
||||
import httpx
|
||||
|
||||
response = httpx.post(
|
||||
f"{service_url}/api/wallet",
|
||||
json={
|
||||
"address": wallet_address,
|
||||
"public_key": wallet_data["public_key"],
|
||||
"wallet_type": "genesis"
|
||||
},
|
||||
timeout=5
|
||||
)
|
||||
|
||||
if response.status_code in (200, 201):
|
||||
print(f"✅ Genesis wallet registered with wallet service")
|
||||
return True
|
||||
else:
|
||||
print(f"⚠️ Failed to register with wallet service: {response.status_code}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ Could not connect to wallet service: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Unified Genesis Block and Wallet Generation")
|
||||
parser.add_argument("--chain-id", default="ait-mainnet", help="Chain ID")
|
||||
parser.add_argument("--proposer", help="Proposer address (defaults to genesis wallet)")
|
||||
parser.add_argument("--create-wallet", action="store_true", help="Create genesis wallet")
|
||||
parser.add_argument("--password", help="Wallet password (auto-generated if not provided)")
|
||||
parser.add_argument("--db-path", default="/var/lib/aitbc/data/chain.db", help="Database path")
|
||||
parser.add_argument("--keystore-path", default="/var/lib/aitbc/keystore/genesis.json", help="Keystore path")
|
||||
parser.add_argument("--genesis-path", default="/var/lib/aitbc/data/ait-mainnet/genesis.json", help="Genesis config path")
|
||||
parser.add_argument("--force", action="store_true", help="Force overwrite existing genesis")
|
||||
parser.add_argument("--register-service", action="store_true", help="Register with wallet service")
|
||||
parser.add_argument("--service-url", default="http://localhost:8003", help="Wallet service URL")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print(f"🌟 Unified Genesis Generation for {args.chain_id}")
|
||||
print("=" * 60)
|
||||
|
||||
# Create genesis wallet
|
||||
if args.create_wallet:
|
||||
print(f"\n📝 Creating Genesis Wallet...")
|
||||
wallet_result = create_genesis_wallet(args.password, args.chain_id)
|
||||
|
||||
print(f"Address: {wallet_result['address']}")
|
||||
print(f"Public key: {wallet_result['public_key']}")
|
||||
print(f"Private key: {wallet_result['private_key']}")
|
||||
print(f"Password: {wallet_result['password']}")
|
||||
|
||||
save_genesis_wallet(wallet_result['wallet'], Path(args.keystore_path), wallet_result['password'])
|
||||
print(f"Wallet saved to: {args.keystore_path}")
|
||||
|
||||
proposer = args.proposer or wallet_result['address']
|
||||
else:
|
||||
proposer = args.proposer or "genesis"
|
||||
wallet_result = None
|
||||
|
||||
# Create genesis block
|
||||
print(f"\n📦 Creating Genesis Block...")
|
||||
genesis_block = create_genesis_block(args.chain_id, proposer)
|
||||
print(f"Height: {genesis_block['height']}")
|
||||
print(f"Hash: {genesis_block['hash']}")
|
||||
print(f"Proposer: {genesis_block['proposer']}")
|
||||
|
||||
# Create allocations
|
||||
print(f"\n💰 Creating Genesis Allocations...")
|
||||
if wallet_result:
|
||||
allocations = create_genesis_allocations(wallet_result['address'])
|
||||
else:
|
||||
allocations = create_genesis_allocations(proposer)
|
||||
|
||||
print(f"Total allocations: {len(allocations)}")
|
||||
for alloc in allocations[:3]: # Show first 3
|
||||
print(f" - {alloc['address']}: {alloc['balance']} AIT")
|
||||
|
||||
# Save genesis configuration
|
||||
print(f"\n💾 Saving Genesis Configuration...")
|
||||
save_genesis_json(genesis_block, allocations, Path(args.genesis_path))
|
||||
print(f"Genesis config saved to: {args.genesis_path}")
|
||||
|
||||
# Initialize database
|
||||
print(f"\n🗄️ Initializing Database...")
|
||||
if initialize_genesis_database(genesis_block, allocations, Path(args.db_path)):
|
||||
print(f"Database initialized: {args.db_path}")
|
||||
|
||||
# Register with wallet service
|
||||
if args.register_service and wallet_result:
|
||||
print(f"\n🔗 Registering with Wallet Service...")
|
||||
register_wallet_with_service(wallet_result['address'], wallet_result['wallet'], args.service_url)
|
||||
|
||||
print(f"\n✅ Unified Genesis Generation Complete!")
|
||||
print(f"\n📋 Summary:")
|
||||
print(f" Chain ID: {args.chain_id}")
|
||||
print(f" Genesis Block: {genesis_block['hash']}")
|
||||
if wallet_result:
|
||||
print(f" Genesis Wallet: {wallet_result['address']}")
|
||||
print(f" Wallet Password: {wallet_result['password']}")
|
||||
print(f" ⚠️ IMPORTANT: Store the password securely!")
|
||||
print(f" Database: {args.db_path}")
|
||||
print(f" Config: {args.genesis_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user