Executive Summary
Building on the foundational framework established in Part 1, this installment advances the system toward production viability by addressing key limitations: static wallet management and single-process execution. We introduce a dynamic wallet ecosystem supporting unlimited generation via BIP39 mnemonic phrases for secure recovery, alongside multi-script decoupling for concurrent operations. A dedicated miner process enables background block production, simulating distributed consensus without full P2P overhead.
Preceding these core upgrades, we implement targeted enhancements: double-spend prevention, CLI wallet selection, and periodic backups for operational resilience. The result is a scalable prototype capable of handling multiple users and miners, with persistence intact.
Key specifications:
- Dynamic Wallets: On-demand generation with 12-word BIP39 mnemonics; restoration from seed.
- Multi-Process Mining: Independent
miner.pyfor continuous PoW; integrates with main chain. - Enhancements: Balance checks on txn submission, indexed wallet picker, auto-backup every 5 blocks.
- Dependencies: Existing (
ecdsa,base58) + new (mnemonic,bip32utilsfor BIP39/BIP32).
New components: 2 scripts (wallet_gen.py, miner.py), ~150 additional LOC. Total framework: ~450 LOC.
System Architecture Updates
The enhanced architecture maintains modularity while introducing process isolation:
| Module/Script | Functionality | Core Components |
|---|---|---|
wallet_gen.py (New) | Bulk mnemonic-based wallet generation and export | BIP39 seed derivation, private key files |
miner.py (New) | Background mining daemon | Interval-based mine_pending(), chain sync |
blockchain.py (Updated) | Extended persistence and validation | add_wallet(), double-spend checks, backups |
main.py (Updated) | Enhanced CLI with wallet indexing | Picker for txns/mining, restore command |
| Existing Modules | Unchanged core logic | Block, PoW, txn, wallet, utils |
Process model: main.py (orchestrator), miner.py (worker), wallet_gen.py (utility)—runnable concurrently via terminals or multiprocessing.
Implementation Details
Pre-Enhancements: Resilience and Integrity
- Double-Spend Prevention: Transaction submission validates sender balance against chain state.
- CLI Wallet Picker: Indexed selection from persisted array; supports “Wallet1” aliases.
- Auto-Backup: Chain snapshot every 5 blocks to
chain_backup_N.json.
Dynamic Wallets with BIP39 Recovery
- Mnemonic Generation: 128-bit entropy → 12 English words (BIP39 standard).
- Derivation Path: Mnemonic → PBKDF2 seed → BIP32 master key → child private/public keys.
- Output: Displays mnemonic (backup), private hex/WIF, public hex, address; exports private to file.
- Restoration:
restore_wallet.pyre-derives from mnemonic, integrates with chain.
Multi-Process Mining
- Daemon Design:
miner.pyloads chain, mines at fixed intervals (default 30s), auto-saves. - Concurrency: Runs parallel to
main.py; shared JSON for state (file locking recommended for prod). - Error Handling: Syncs on startup; retries failed mines.
Source Code Listings
Updated blockchain.py (Enhancements)
from block import Block
from proof_of_work import proof_of_work
from transaction import Transaction
from utils import GENESIS_DATA, calculate_reward, DIFFICULTY, HALVING_INTERVAL
from wallet import Wallet
from ecdsa import SigningKey, SECP256k1
import json
import os # For backups
class Blockchain:
def __init__(self):
self.difficulty = DIFFICULTY
self.chain = [self._create_genesis_block()]
self.pending_transactions = []
# ... (existing _create_genesis_block, get_latest_block, mine_pending, is_valid_chain, get_balance, save_chain, load_chain)
def add_transaction(self, txn):
# ENHANCEMENT: Double-spend prevention
sender_balance = self.get_balance(txn.from_addr)
if txn.from_addr != "network" and sender_balance < txn.amount:
raise ValueError(f"Insufficient balance: {sender_balance} < {txn.amount}")
self.pending_transactions.append(txn)
def add_wallet(self, wallet):
# ENHANCEMENT: Dynamic wallet append
all_wallets = self.load_wallets() + [wallet]
self.save_wallets(all_wallets)
def get_all_wallets(self):
return self.load_wallets()
def mine_pending(self, miner_wallet):
# ... (existing logic)
self.chain.append(new_block)
self.pending_transactions = []
if len(self.chain) % HALVING_INTERVAL == 0:
print(f"Halving! New reward: {calculate_reward(len(self.chain))}")
self.save_chain()
# ENHANCEMENT: Auto-backup every 5 blocks
if len(self.chain) % 5 == 0:
backup_file = f'chain_backup_{len(self.chain)}.json'
self.save_chain(backup_file)
print(f"Backup: {backup_file}")
print(f"Auto-saved after mining Block {new_block.index}")
# ... (existing save/load_wallets)New: wallet_gen.py – Dynamic Mnemonic Wallets
from mnemonic import Mnemonic # pip install mnemonic
from bip32utils import BIP32Key # pip install bip32utils
from ecdsa import SigningKey, SECP256k1 # For key creation
from wallet import Wallet # FIXED: Missing import for address generation
from blockchain import Blockchain # For persistence
import sys
import hashlib
import base58
def generate_wallets(n=1):
mnemo = Mnemonic("english")
bc = Blockchain()
new_wallets = []
for i in range(n):
# Step 1: Generate 12-word mnemonic
mnemonic = mnemo.generate(strength=128) # 128-bit entropy
print(f"\nWallet {i + 1} Mnemonic (backup these words!): {mnemonic}")
# Step 2: Derive seed from mnemonic (BIP39)
seed = mnemo.to_seed(mnemonic)
# Step 3: Derive master private key (BIP32)
master_key = BIP32Key.fromEntropy(seed)
private_key_hex = master_key.PrivateKey().hex()
# Step 4: Create Wallet instance for address
try:
w = Wallet()
w.private_key = SigningKey.from_string(bytes.fromhex(private_key_hex), curve=SECP256k1)
w.public_key = w.private_key.get_verifying_key()
w.address = w.generate_address()
# Display keys
print(f"Private Key (hex): {private_key_hex}")
print(f"Public Key (hex): {w.public_key.to_string().hex()}")
print(f"Address: {w.address}")
# Secure export (warn user)
print("WARNING: Back up mnemonic privately. Never share keys.")
# Export private key to file (for demo; encrypt in prod)
with open(f"private_{w.address[:8]}.key", 'w') as f:
f.write(private_key_hex)
print(f"Wallet {i + 1} created successfully!")
new_wallets.append(w)
except Exception as e:
print(f"Key derivation error for wallet {i + 1}: {e}")
continue # Skip bad one
# Persist to wallets.json (append)
all_wallets = bc.load_wallets() + new_wallets
bc.save_wallets(all_wallets)
print(f"\nGenerated {len(new_wallets)} wallets (total persisted: {len(all_wallets)}).")
if __name__ == "__main__":
n = int(sys.argv[1]) if len(sys.argv) > 1 else 5
generate_wallets(n)Output:
Wallet 1 Mnemonic (backup these words!): second rough sea kick frown item hold uncle rookie noodle negative sheriff
Private Key (hex): e0fb92bbac4fedc8bf23a9175e80dda98583de60ce7a887a070a8a84b60217be
Public Key (hex): 202485758b173d470a122a480ce7b011e6f4727883325288628a44bdfd2a7bbe503bbed5df229e6913263cb6e82b094920fc5ee3700bc78e90f78447769f0de6
Address: 13ZKyMtbBEkosGYhkij7MS51QQtBLgX3fe
WARNING: Back up mnemonic privately. Never share keys.
Wallet 1 created successfully!
Wallet 2 Mnemonic (backup these words!): drip burst salute combine same traffic spare birth emotion such offer huge
Private Key (hex): 024365026aee9f6938f9ea17d11978fbc4635c8a5c2d0f94ae9dd7876659eb88
Public Key (hex): 3c39fb72f4c4be6d270f0d81889a06d574237133a02a77da1fcaccb324cbcf0a2fb02104f3c48a7dd9306206493cef352ed9e15f9cded88de5186a553587a04a
Address: 13dU8JU5cYj1LeLbsgisq4f5qNDTNrKJSt
WARNING: Back up mnemonic privately. Never share keys.
Wallet 2 created successfully!
Wallet 3 Mnemonic (backup these words!): easy extend float vintage salad stone kind pony insane about nest pluck
Private Key (hex): 528d3dd831e328ee244befaaed4d66e6c5013603d17884ddb7509bcb6d079efc
Public Key (hex): eb1e93e764dac414fd057b5b8e0c7b0c3fd733a1ce03262444a13e7b06a26f6fb7d7b0ca21f6fc0b0630ce42444a54124f0f3d2923cb0a6e276583be14b27b7a
Address: 1BvUUCTgwXx7YMx7CHKxFd2AvXJ9VG5q86
WARNING: Back up mnemonic privately. Never share keys.
Wallet 3 created successfully!
Wallet 4 Mnemonic (backup these words!): hover uniform remove blue beyond target alter virtual journey cherry pledge verb
Private Key (hex): 00d384515ff69ab75434344683d9a93f20f6ab69ef1e0ef729451573c1a7933c
Public Key (hex): 511b9c3d512b832955e8319f938a4917745f371316200f5dc33adbc51a1322817fbf775d756b1c6d48c2b7efedd19bfe0249d8bf7094f0f33c2b1c4cd8e9d728
Address: 1HntHo2TsykStcyn7MTUGHtFEJGUQJLhRh
WARNING: Back up mnemonic privately. Never share keys.
Wallet 4 created successfully!
Wallet 5 Mnemonic (backup these words!): cluster junk tray toddler announce acquire install walnut learn opera sentence market
Private Key (hex): e4064035ba97d58141bc3c2e2348071c8299fdf5df375ba8217d3c617b0622a1
Public Key (hex): dd5028cd3fe3f64ee07be2eef426af4c8603b83737b11a7d23f1d295cd6501c4984d68d417a395eeafdb63a3a3e48abe822a87d53516e23976121af260d5308e
Address: 1M5YnDRfp3C7Mfb5T12tKdZXKqry1hAs5X
WARNING: Back up mnemonic privately. Never share keys.
Wallet 5 created successfully!
Loaded 2 persisted wallets.
Wallets saved to wallets.json
Generated 5 wallets (total persisted: 7).
New: miner.py – Multi-Process Mining Daemon
import time
from blockchain import Blockchain
def run_miner(miner_address, interval=30):
bc = Blockchain()
bc.load_chain()
miner_wallet = Wallet()
miner_wallet.address = miner_address
print(f"Miner daemon started for {miner_address}. Interval: {interval}s")
block_count = len(bc.chain)
while True:
bc.mine_pending(miner_wallet)
print(f"Mined Block {len(bc.chain)} | Balance: {bc.get_balance(miner_address)}")
time.sleep(interval)
if __name__ == "__main__":
address = input("Miner address: ").strip()
run_miner(address)Updated main.py (CLI Picker)
# ... (existing imports and __init__)
# In while loop:
elif cmd == "txn":
try:
wallets = bc.get_all_wallets()
print("Wallets:")
for i, w in enumerate(wallets):
print(f"{i}: {w.address} (Bal: {bc.get_balance(w.address)})")
from_i = int(input("From index: "))
to_i = int(input("To index: "))
amount = float(input("Amount: "))
new_tx = Transaction(wallets[from_i].address, wallets[to_i].address, amount)
new_tx.sign_tx(wallets[from_i])
bc.add_transaction(new_tx)
print("Added to pending!")
except (ValueError, IndexError) as e:
print(f"Error: {e}")
elif cmd == "mined":
wallets = bc.get_all_wallets()
print("Wallets:")
for i, w in enumerate(wallets):
print(f"{i}: {w.address}")
miner_i = int(input("Miner index: "))
bc.mine_pending(wallets[miner_i])
print(f"Mined! Length: {len(bc.chain)}")restore_wallet.py (New: Recovery Tool)
from mnemonic import Mnemonic # pip install mnemonic
from bip32utils import BIP32Key # pip install bip32utils
from ecdsa import SigningKey, SECP256k1
from wallet import Wallet # For address derivation
import sys
import base58
import hashlib
def restore_wallet(input_type, input_value, bc=None):
"""Restore from mnemonic or private key hex; optional balance query."""
if input_type == "mnemonic":
mnemo = Mnemonic("english")
if not mnemo.check(input_value):
raise ValueError("Invalid mnemonic—check spelling.")
# Derive seed (BIP39)
seed = mnemo.to_seed(input_value)
# Derive master private key (BIP32)
master_key = BIP32Key.fromEntropy(seed)
private_key_hex = master_key.PrivateKey().hex()
mnemonic = input_value
elif input_type == "private_key":
# Validate hex (64 chars)
if len(input_value) != 64 or not all(c in '0123456789abcdefABCDEF' for c in input_value):
raise ValueError("Invalid private key hex—must be 64 hex chars.")
private_key_hex = input_value.lower()
mnemonic = "N/A (direct private key)"
else:
raise ValueError("Input type must be 'mnemonic' or 'private_key'.")
# Create restored wallet
w = Wallet()
w.private_key = SigningKey.from_string(bytes.fromhex(private_key_hex), curve=SECP256k1)
w.public_key = w.private_key.get_verifying_key()
w.address = w.generate_address()
# Derive WIF private key (compressed, mainnet)
wif_bytes = b'\x80' + bytes.fromhex(private_key_hex) + b'\x01' # Compressed
wif_private = base58.b58encode_check(wif_bytes).decode()
# Output all info
print("\n=== Restored Wallet Information ===")
print(f"Mnemonic (backup words): {mnemonic}")
print(f"Private Key (hex): {private_key_hex}")
print(f"Private Key (WIF): {wif_private}")
print(f"Public Key (compressed hex): {w.public_key.to_string('compressed').hex()}")
print(f"Address: {w.address}")
if bc:
print(f"Current Balance: {bc.get_balance(w.address)}")
print("=== End Restoration ===")
return w
def print_help():
print("Usage: python restore_wallet.py [type] [value]")
print("Type: 'mnemonic' or 'private_key'")
print("Value: 12-24 words for mnemonic, or 64-hex chars for private key")
print("Example: python restore_wallet.py mnemonic 'abandon ability able ...'")
print("Interactive: Run without args for prompts.")
if __name__ == "__main__":
if len(sys.argv) == 2 and sys.argv[1] == "--help":
print_help()
sys.exit(0)
if len(sys.argv) > 2:
# Arg mode: sys.argv[1] = type, sys.argv[2:] = value
input_type = sys.argv[1]
input_value = ' '.join(sys.argv[2:])
else:
# Interactive
input_type = input("Restore type (mnemonic/private_key): ").strip().lower()
if input_type not in ['mnemonic', 'private_key']:
print("Invalid type—use 'mnemonic' or 'private_key'.")
print_help()
sys.exit(1)
input_value = input("Enter value: ").strip()
try:
restored = restore_wallet(input_type, input_value)
print("Restoration successful—use address for transactions.")
except ValueError as e:
print(f"Error: {e}")
except Exception as e:
print(f"Unexpected error: {e}")Output:
Restore type (mnemonic/private_key): private_key
Enter value: 4c4346e5798584b3345f63cd53b20d0782e086c8a879f129da3216bd46d662d0
=== Restored Wallet Information ===
Mnemonic (backup words): N/A (direct private key)
Private Key (hex): 4c4346e5798584b3345f63cd53b20d0782e086c8a879f129da3216bd46d662d0
Private Key (WIF): KymxPZhGX3ybKJ6NTEH1uG333YVeprdfDAkjDQ4AZWqbQD7GXbB3
Public Key (compressed hex): 03fa9f2eefd1b4147aad484cbf891bd92b28bb9f9b0b66c8a6bbba90f4331fd9e3
Address: 1JCCKoqL869fhL4RXNbdVeumoKJU6KGqkv
=== End Restoration ===
Restoration successful—use address for transactions.
Updated requirements.txt
ecdsa==0.18.0
base58==2.1
mnemonic==0.20
bip32utils==0.3.0.post1
Deployment and Validation Procedures
- Initialization:
- Update files from Part 1.
- Add new scripts.
pip install -r requirements.txt.
- Execution:
python main.py: Enhanced CLI.- Terminal 1:
python main.py. - Terminal 2:
python wallet_gen.py 5(5 new wallets). - Terminal 3:
python miner.py(enter address) → Background mining.
- Validation Tests:
- Dynamic Wallets: Generate 3, list in CLI—total 5, balances 0.
- Mining Process: Miner runs 2 blocks—main CLI shows length +2.
- Recovery:
python restore_wallet.py "abandon abandon ..."→ Matches generated address. - Double-Spend: Txn > balance → ValueError.
- Backup: Mine 5 blocks →
chain_backup_5.jsoncreated.
Sample Execution Outputs
Wallet Generation (python wallet_gen.py 1)
“`
Wallet 1 Mnemonic: abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
Private Key (hex): e9873d79c6d87