✅ Data Directory Restructure - Moved /opt/aitbc/data to /var/lib/aitbc/data (proper system location) - Updated all production services to use system data path - Updated systemd services to use system data path - Created symlink for backward compatibility - Created proper data directories in /var/lib/aitbc/data/ ✅ Services Updated - Marketplace: /var/lib/aitbc/data/marketplace - Blockchain: /var/lib/aitbc/data/blockchain - OpenClaw: /var/lib/aitbc/data/openclaw - All services now using system data paths ✅ System Compliance - Data stored in /var/lib/aitbc (FHS compliant) - Repository no longer contains runtime data - Backward compatibility maintained with symlink - Production services using correct system paths 🚀 AITBC now follows proper system data directory structure!
323 lines
11 KiB
Python
Executable File
323 lines
11 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Real Blockchain with Mining and Multi-Chain Support
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import time
|
|
import hashlib
|
|
import logging
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
from typing import Dict, List, Optional
|
|
import threading
|
|
|
|
# Production logging
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s [%(levelname)s] %(name)s: %(message)s',
|
|
handlers=[
|
|
logging.FileHandler('/opt/aitbc/production/logs/blockchain/mining.log'),
|
|
logging.StreamHandler()
|
|
]
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class ProofOfWork:
|
|
"""Real Proof of Work mining algorithm"""
|
|
|
|
def __init__(self, difficulty: int = 4):
|
|
self.difficulty = difficulty
|
|
self.target = "0" * difficulty
|
|
|
|
def mine(self, block_data: dict) -> tuple:
|
|
"""Mine a block with real proof of work"""
|
|
nonce = 0
|
|
start_time = time.time()
|
|
|
|
while True:
|
|
# Create block hash with nonce
|
|
content = f"{json.dumps(block_data, sort_keys=True)}{nonce}"
|
|
block_hash = hashlib.sha256(content.encode()).hexdigest()
|
|
|
|
# Check if hash meets difficulty
|
|
if block_hash.startswith(self.target):
|
|
mining_time = time.time() - start_time
|
|
logger.info(f"Block mined! Nonce: {nonce}, Hash: {block_hash[:16]}..., Time: {mining_time:.2f}s")
|
|
return block_hash, nonce, mining_time
|
|
|
|
nonce += 1
|
|
|
|
# Prevent infinite loop
|
|
if nonce > 10000000:
|
|
raise Exception("Mining failed - nonce too high")
|
|
|
|
class MultiChainManager:
|
|
"""Multi-chain blockchain manager"""
|
|
|
|
def __init__(self):
|
|
self.chains = {}
|
|
self.miners = {}
|
|
self.node_id = os.getenv('NODE_ID', 'aitbc')
|
|
self.data_dir = Path(f'/var/lib/aitbc/data/blockchain/{self.node_id}')
|
|
self.data_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Initialize multiple chains
|
|
self._initialize_chains()
|
|
|
|
logger.info(f"Multi-chain manager initialized for node: {self.node_id}")
|
|
|
|
def _initialize_chains(self):
|
|
"""Initialize multiple blockchain chains"""
|
|
chains_config = [
|
|
{
|
|
'name': 'aitbc-main',
|
|
'difficulty': 4,
|
|
'block_reward': 50.0,
|
|
'description': 'Main AITBC blockchain'
|
|
},
|
|
{
|
|
'name': 'aitbc-gpu',
|
|
'difficulty': 3,
|
|
'block_reward': 25.0,
|
|
'description': 'GPU computing blockchain'
|
|
}
|
|
]
|
|
|
|
for chain_config in chains_config:
|
|
chain_name = chain_config['name']
|
|
self.chains[chain_name] = {
|
|
'name': chain_name,
|
|
'blocks': [],
|
|
'difficulty': chain_config['difficulty'],
|
|
'block_reward': chain_config['block_reward'],
|
|
'description': chain_config['description'],
|
|
'pending_transactions': [],
|
|
'balances': {},
|
|
'mining_stats': {
|
|
'blocks_mined': 0,
|
|
'total_mining_time': 0,
|
|
'average_mining_time': 0
|
|
}
|
|
}
|
|
|
|
# Create miner for this chain
|
|
self.miners[chain_name] = ProofOfWork(chain_config['difficulty'])
|
|
|
|
# Load existing chain data
|
|
self._load_chain(chain_name)
|
|
|
|
# Create genesis block if empty
|
|
if not self.chains[chain_name]['blocks']:
|
|
self._create_genesis_block(chain_name)
|
|
|
|
logger.info(f"Chain {chain_name} initialized with {len(self.chains[chain_name]['blocks'])} blocks")
|
|
|
|
def _load_chain(self, chain_name: str):
|
|
"""Load existing chain data"""
|
|
chain_file = self.data_dir / f'{chain_name}.json'
|
|
|
|
try:
|
|
if chain_file.exists():
|
|
with open(chain_file, 'r') as f:
|
|
data = json.load(f)
|
|
|
|
self.chains[chain_name] = data
|
|
logger.info(f"Loaded chain {chain_name} with {len(data.get('blocks', []))} blocks")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to load chain {chain_name}: {e}")
|
|
|
|
def _save_chain(self, chain_name: str):
|
|
"""Save chain data"""
|
|
try:
|
|
chain_file = self.data_dir / f'{chain_name}.json'
|
|
|
|
with open(chain_file, 'w') as f:
|
|
json.dump(self.chains[chain_name], f, indent=2)
|
|
|
|
logger.debug(f"Chain {chain_name} saved")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to save chain {chain_name}: {e}")
|
|
|
|
def _create_genesis_block(self, chain_name: str):
|
|
"""Create genesis block for chain"""
|
|
chain = self.chains[chain_name]
|
|
|
|
genesis_data = {
|
|
'index': 0,
|
|
'timestamp': time.time(),
|
|
'data': {
|
|
'type': 'genesis',
|
|
'chain': chain_name,
|
|
'node_id': self.node_id,
|
|
'description': chain['description'],
|
|
'block_reward': chain['block_reward']
|
|
},
|
|
'previous_hash': '0',
|
|
'nonce': 0
|
|
}
|
|
|
|
# Mine genesis block
|
|
block_hash, nonce, mining_time = self.miners[chain_name].mine(genesis_data)
|
|
|
|
genesis_block = {
|
|
'index': 0,
|
|
'timestamp': genesis_data['timestamp'],
|
|
'data': genesis_data['data'],
|
|
'previous_hash': '0',
|
|
'hash': block_hash,
|
|
'nonce': nonce,
|
|
'mining_time': mining_time,
|
|
'miner': self.node_id
|
|
}
|
|
|
|
chain['blocks'].append(genesis_block)
|
|
chain['mining_stats']['blocks_mined'] = 1
|
|
chain['mining_stats']['total_mining_time'] = mining_time
|
|
chain['mining_stats']['average_mining_time'] = mining_time
|
|
|
|
# Initialize miner balance with block reward
|
|
chain['balances'][f'miner_{self.node_id}'] = chain['block_reward']
|
|
|
|
self._save_chain(chain_name)
|
|
|
|
logger.info(f"Genesis block created for {chain_name} - Reward: {chain['block_reward']} AITBC")
|
|
|
|
def mine_block(self, chain_name: str, transactions: List[dict] = None) -> dict:
|
|
"""Mine a new block on specified chain"""
|
|
if chain_name not in self.chains:
|
|
raise Exception(f"Chain {chain_name} not found")
|
|
|
|
chain = self.chains[chain_name]
|
|
|
|
# Prepare block data
|
|
block_data = {
|
|
'index': len(chain['blocks']),
|
|
'timestamp': time.time(),
|
|
'data': {
|
|
'transactions': transactions or [],
|
|
'chain': chain_name,
|
|
'node_id': self.node_id
|
|
},
|
|
'previous_hash': chain['blocks'][-1]['hash'] if chain['blocks'] else '0'
|
|
}
|
|
|
|
# Mine the block
|
|
block_hash, nonce, mining_time = self.miners[chain_name].mine(block_data)
|
|
|
|
# Create block
|
|
new_block = {
|
|
'index': block_data['index'],
|
|
'timestamp': block_data['timestamp'],
|
|
'data': block_data['data'],
|
|
'previous_hash': block_data['previous_hash'],
|
|
'hash': block_hash,
|
|
'nonce': nonce,
|
|
'mining_time': mining_time,
|
|
'miner': self.node_id,
|
|
'transactions_count': len(transactions or [])
|
|
}
|
|
|
|
# Add to chain
|
|
chain['blocks'].append(new_block)
|
|
|
|
# Update mining stats
|
|
chain['mining_stats']['blocks_mined'] += 1
|
|
chain['mining_stats']['total_mining_time'] += mining_time
|
|
chain['mining_stats']['average_mining_time'] = (
|
|
chain['mining_stats']['total_mining_time'] / chain['mining_stats']['blocks_mined']
|
|
)
|
|
|
|
# Reward miner
|
|
miner_address = f'miner_{self.node_id}'
|
|
if miner_address not in chain['balances']:
|
|
chain['balances'][miner_address] = 0
|
|
chain['balances'][miner_address] += chain['block_reward']
|
|
|
|
# Process transactions
|
|
for tx in transactions or []:
|
|
self._process_transaction(chain, tx)
|
|
|
|
self._save_chain(chain_name)
|
|
|
|
logger.info(f"Block mined on {chain_name} - Reward: {chain['block_reward']} AITBC")
|
|
|
|
return new_block
|
|
|
|
def _process_transaction(self, chain: dict, transaction: dict):
|
|
"""Process a transaction"""
|
|
from_addr = transaction.get('from_address')
|
|
to_addr = transaction.get('to_address')
|
|
amount = transaction.get('amount', 0)
|
|
|
|
# Initialize balances
|
|
if from_addr not in chain['balances']:
|
|
chain['balances'][from_addr] = 1000.0 # Initial balance
|
|
if to_addr not in chain['balances']:
|
|
chain['balances'][to_addr] = 0.0
|
|
|
|
# Process transaction
|
|
if chain['balances'][from_addr] >= amount:
|
|
chain['balances'][from_addr] -= amount
|
|
chain['balances'][to_addr] += amount
|
|
logger.info(f"Transaction processed: {amount} AITBC from {from_addr} to {to_addr}")
|
|
|
|
def get_chain_info(self, chain_name: str) -> dict:
|
|
"""Get chain information"""
|
|
if chain_name not in self.chains:
|
|
return {'error': f'Chain {chain_name} not found'}
|
|
|
|
chain = self.chains[chain_name]
|
|
|
|
return {
|
|
'chain_name': chain_name,
|
|
'blocks': len(chain['blocks']),
|
|
'difficulty': chain['difficulty'],
|
|
'block_reward': chain['block_reward'],
|
|
'description': chain['description'],
|
|
'mining_stats': chain['mining_stats'],
|
|
'total_addresses': len(chain['balances']),
|
|
'total_balance': sum(chain['balances'].values()),
|
|
'latest_block': chain['blocks'][-1] if chain['blocks'] else None
|
|
}
|
|
|
|
def get_all_chains_info(self) -> dict:
|
|
"""Get information about all chains"""
|
|
return {
|
|
'node_id': self.node_id,
|
|
'total_chains': len(self.chains),
|
|
'chains': {name: self.get_chain_info(name) for name in self.chains.keys()}
|
|
}
|
|
|
|
if __name__ == '__main__':
|
|
# Initialize multi-chain manager
|
|
manager = MultiChainManager()
|
|
|
|
# Mine blocks on all chains
|
|
for chain_name in manager.chains.keys():
|
|
try:
|
|
# Create sample transactions
|
|
transactions = [
|
|
{
|
|
'from_address': f'user_{manager.node_id}',
|
|
'to_address': f'user_other',
|
|
'amount': 10.0,
|
|
'data': {'type': 'payment'}
|
|
}
|
|
]
|
|
|
|
# Mine block
|
|
block = manager.mine_block(chain_name, transactions)
|
|
print(f"Mined block on {chain_name}: {block['hash'][:16]}...")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to mine block on {chain_name}: {e}")
|
|
|
|
# Print chain information
|
|
info = manager.get_all_chains_info()
|
|
print(f"Multi-chain info: {json.dumps(info, indent=2)}")
|