def get_transaction(tx_hash): blockchain = Blockchain() transaction = blockchain.get_transaction_by_hash(tx_hash) if transaction: return json.dumps(transaction.to_dict()) response.status = 404 return json.dumps({'success': False, 'reason': 'Transaction Not Found'})
def get_block_header_by_hash(block_hash): blockchain = Blockchain() block_header = blockchain.get_block_header_by_hash(block_hash) if block_header is None: response.status = 404 return json.dumps({'success': False, 'reason': 'Block Not Found'}) return json.dumps(block_header.to_dict())
def get_transactions_index(block_hash): blockchain = Blockchain() transaction_inv = blockchain.get_transaction_hashes_by_block_hash( block_hash) if transaction_inv: return json.dumps({'tx_hashes': transaction_inv}) response.status = 404 return json.dumps({'success': False, 'reason': 'Transactions Not Found'})
def get_block_header_by_height(height): blockchain = Blockchain() if height == "latest": block_header = blockchain.get_tallest_block_header() else: block_header = blockchain.get_block_headers_by_height(height) if block_header is None: response.status = 404 return json.dumps({'success': False, 'reason': 'Block Not Found'}) return json.dumps(block_header.to_dict())
def get_blocks_inv(start_block_height, end_block_height): blockchain = Blockchain() blocks_range = end_block_height - start_block_height if blocks_range < 1 or blocks_range > 500: response.status = 400 return json.dumps({'success': False, 'reason': 'Bad request'}) blocks_inv = blockchain.get_hashes_range(start_block_height, end_block_height) if blocks_inv: return json.dumps({'blocks_inv': blocks_inv}) response.status = 404 return json.dumps({'success': False, 'reason': 'Blocks not found'})
def full(): helptext = ''' Available commands: =================== balance <public key (optional)> history <public key (optional)> getnodes getblock <index (optional)> getblocks <start index (optional)> <stop index (optional)> mempoolcount getmempool getunconfirmedtx <tx hash> mine <start | stop> quit or exit ''' peers = Peers() api_client = ApiClient(peers) blockchain = Blockchain() mempool = Mempool() validator = Validator() ip = config['user']['ip'] public_key = config['user']['public_key'] if ip is None or public_key is None: print("\n\npublic key and IP must be provided.\n\n") sys.exit(1) else: print("\n\nfull node starting...\n\n") full_node = FullNode(peers, api_client, blockchain, mempool, validator) full_node.start() miner = Miner(blockchain, mempool) mining = False while True: cmd = input("{} ({}) full node > ".format( config['network']['name'], config['network']['ticker_symbol'])) cmd_split = cmd.split() try: if cmd_split[0] == "balance": if len(cmd_split) == 2: url = full_node.BALANCE_URL.format( "localhost", full_node.FULL_NODE_PORT, cmd_split[1]) else: url = full_node.BALANCE_URL.format( "localhost", full_node.FULL_NODE_PORT, public_key) response = requests.get(url) print(response.json()) elif cmd_split[0] == "history": if len(cmd_split) == 2: url = full_node.TRANSACTION_HISTORY_URL.format( "localhost", full_node.FULL_NODE_PORT, cmd_split[1]) response = requests.get(url) else: url = full_node.TRANSACTION_HISTORY_URL.format( "localhost", full_node.FULL_NODE_PORT, public_key) response = requests.get(url) print(response.json()) elif cmd_split[0] == "getnodes": url = full_node.NODES_URL.format("localhost", full_node.FULL_NODE_PORT) response = requests.get(url) print(response.json()) elif cmd_split[0] == "getblock": if len(cmd_split) == 2: url = full_node.BLOCKS_URL.format("localhost", full_node.FULL_NODE_PORT, cmd_split[1]) else: url = full_node.BLOCKS_URL.format("localhost", full_node.FULL_NODE_PORT, "latest") response = requests.get(url) print(response.json()) elif cmd_split[0] == "getblocks": if len(cmd_split) == 3: url = full_node.BLOCKS_INV_URL.format( "localhost", full_node.FULL_NODE_PORT, cmd_split[1], cmd_split[2]) else: url = full_node.BLOCKS_URL.format("localhost", full_node.FULL_NODE_PORT, "") response = requests.get(url) print(response.json()) elif cmd_split[0] == "mempoolcount": url = full_node.UNCONFIRMED_TRANSACTIONS_URL.format( "localhost", full_node.FULL_NODE_PORT, "count") response = requests.get(url) print(response.json()) elif cmd_split[0] == "getmempool": url = full_node.UNCONFIRMED_TRANSACTIONS_URL.format( "localhost", full_node.FULL_NODE_PORT, "") response = requests.get(url) print(response.json()) elif cmd_split[0] == "getunconfirmedtx": if len(cmd_split) == 2: url = full_node.UNCONFIRMED_TRANSACTIONS_URL.format( "localhost", full_node.FULL_NODE_PORT, cmd_split[1]) response = requests.get(url) print(response.json()) else: print("\nRequires tx hash\n") elif cmd_split[0] == "mine": if len(cmd_split) == 2: if cmd_split[1] == "start": if mining is False: print("\n\nminer starting...\n\n") mining = True miner.start() elif cmd_split[1] == "stop": if mining is True: print("\n\nminer shutting down...\n\n") mining = False miner.shutdown() else: print("\nRequires: start | stop") else: print("\nRequires: start | stop") elif cmd_split[0] in ("quit", "exit"): if mining is True: print("\n\nminer shutting down...\n\n") miner.shutdown() time.sleep(2) full_node.shutdown() time.sleep(2) sys.exit(0) else: # help print(helptext) except IndexError: pass
def get_transaction_history(address): blockchain = Blockchain() transaction_history = blockchain.get_transaction_history(address) return json.dumps( [transaction.to_json() for transaction in transaction_history])
def get_balance(address): blockchain = Blockchain() return json.dumps(blockchain.get_balance(address))
def get_height(): blockchain = Blockchain() return json.dumps({"height": blockchain.get_height()})
class Validator(object): def __init__(self): self.blockchain = Blockchain() self.mempool = Mempool() def check_hash_and_hash_pattern(self, block): hash_difficulty = self.blockchain.calculate_hash_difficulty( block.height) if block.block_header.hash[:hash_difficulty].count( '0') != hash_difficulty: raise InvalidHash( block.height, "Incompatible Block Hash: {}".format(block.block_header.hash)) return def check_height_and_previous_hash(self, block): previous_block = self.blockchain.get_block_header_by_hash( block.block_header.previous_hash) if previous_block is None: raise ChainContinuityError( block.height, "Incompatible block hash: {} and hash: {}".format( block.height - 1, block.block_header.previous_hash)) previous_block_header, previous_block_branch, previous_block_height = previous_block if previous_block_height != block.height - 1: raise ChainContinuityError( block.height, "Incompatible block height: {}".format(block.height - 1)) return def check_block_reward(self, block): reward_amount = self.blockchain.get_reward(block.height) for transaction in block.transactions[1:]: if TransactionType( transaction.tx_type) == TransactionType.COINBASE: logger.warning("Block not valid. Multiple coinbases detected") return False reward_amount += transaction.fee # first transaction is coinbase reward_transaction = block.transactions[0] if TransactionType( reward_transaction.tx_type) != TransactionType.COINBASE: logger.warning("Block not valid. Missing coinbase") return False if reward_transaction.amount != reward_amount: logger.warning("Invalid block reward {} should be {}".format( reward_transaction.amount, reward_amount)) return False if reward_transaction.source != "0": logger.warning("Invalid Coinbase source") return False return True def validate_block_header(self, block_header, transactions_inv): if self.blockchain.get_block_header_by_hash(block_header.hash): logger.warning('Block Header already exists') return False if block_header.version != config['network']['version']: logger.warning('Incompatible version') return False if block_header.merkle_root != self.calculate_merkle_root( transactions_inv): logger.warning('Invalid merkle root') return False previous_block = self.blockchain.get_block_header_by_hash( block_header.previous_hash) if previous_block is None: return None previous_block_header, previous_block_branch, previous_block_height = previous_block if self.blockchain.calculate_hash_difficulty( previous_block_height + 1) > block_header.hash_difficulty: logger.warning('Invalid hash difficulty') return False return previous_block_height + 1 def validate_block(self, block, merkle_root): if block.block_header.merkle_root != merkle_root: logger.warning("invalid merkle root") return False if not self.check_block_reward(block): logger.warning("Invalid block reward") return False return True def validate_block_transactions_inv(self, transactions_inv): """ Checks a list of transaction hashes, checks for double-spends and/or entries in the mempool Returns a list of unknown transaction hashes :param transactions_inv: :return: block_transactions, missing_transactions_inv :rtype: tuple(list, list) """ missing_transactions_inv = [] block_transactions = [] for tx_hash in transactions_inv: if self.blockchain.find_duplicate_transactions(tx_hash): logger.warning( 'Transaction not valid. Double-spend prevented: {}'. format(tx_hash)) return False transaction = self.mempool.get_unconfirmed_transaction(tx_hash) if transaction is None: missing_transactions_inv.append(tx_hash) else: block_transactions.append(transaction) return block_transactions, missing_transactions_inv def validate_transaction(self, transaction): """ Validate a single transaction. Check for double-spend, invalid signature, and insufficient funds :param transaction: :return: boolean :rtype: boolean """ if self.blockchain.find_duplicate_transactions(transaction.tx_hash): logger.warning( 'Transaction not valid. Double-spend prevented: {}'.format( transaction.tx_hash)) return False if not transaction.verify(): logger.warning( 'Transaction not valid. Invalid transaction signature: {}'. format(transaction.tx_hash)) return False balance = self.blockchain.get_balance(transaction.source) if transaction.amount + transaction.fee > balance: logger.warning( 'Transaction not valid. Insufficient funds: {} {}<{}'.format( transaction.tx_hash, balance, transaction.amount + transaction.fee)) return False return True @staticmethod def calculate_merkle_root(tx_hashes): coinbase_hash = tx_hashes[0] merkle_base = sorted(tx_hashes[1:]) merkle_base.insert(0, coinbase_hash) while len(merkle_base) > 1: temp_merkle_base = [] for i in range(0, len(merkle_base), 2): if i == len(merkle_base) - 1: temp_merkle_base.append( hashlib.sha256(merkle_base[i]).hexdigest()) else: temp_merkle_base.append( hashlib.sha256(merkle_base[i] + merkle_base[i + 1]).hexdigest()) merkle_base = temp_merkle_base return merkle_base[0]
def __init__(self): self.blockchain = Blockchain() self.mempool = Mempool()