def set_consensus_chain(self): # 通过POW机制选取nonce最大的链作为公共链 for block_index in list(self.candidate_blocks.keys()): if block_index <= db.get_block_height( self.get_wallet_address()) - 1: curr_block = db.get_block_data_by_index( self.get_wallet_address(), block_index) max_nonce_block = curr_block for candidate_block in self.candidate_blocks[block_index]: if (candidate_block.previous_hash == curr_block.previous_hash) and ( candidate_block.nonce > max_nonce_block.nonce): max_nonce_block = candidate_block # 校验每一笔交易 valid_flag = True for idx in range(len(max_nonce_block.transactions)): tx = max_nonce_block.transactions[-1 - idx] if not self.verify_transaction(tx): valid_flag = False if valid_flag and max_nonce_block.current_hash != curr_block.current_hash: print('[Info] consensusing, replace with new block', max_nonce_block.current_hash) db.write_to_db(self.get_wallet_address(), max_nonce_block)
def handle_sendblock(self, payload): new_block = payload with self.server.node_manager.lock: blockchain = self.server.node_manager.blockchain block_height = db.get_block_height(blockchain.wallet.address) latest_block = db.get_block_data_by_index( blockchain.get_wallet_address(), block_height - 1) if (latest_block.current_hash == new_block.previous_hash) and (latest_block.index + 1 == new_block.index): # 校验交易是否有效 is_valid = True for idx in range(len(new_block.transactions)): tx = new_block.transactions[-1 - idx] if not blockchain.verify_transaction(tx): is_valid = False break if is_valid: db.write_to_db(blockchain.wallet.address, new_block) # 重新挖矿 blockchain.current_transactions = [] db.clear_unconfirmed_tx_from_disk( blockchain.wallet.address) else: self.add_to_candidate_blocks(blockchain, new_block) blockchain.set_consensus_chain()
def bootstrap(self, seed_nodes=[]): """ 根据初始节点引导初始化 :param seed_nodes:<Node list> 种子节点列表 :return: """ for seed_node in seed_nodes: #判断是否已经存在在邻居中 flag = self.tablsp.judnei(seed_node) #不同的格式怎么处理todo print self.tablsp.basetable[1] if flag == 0: #不在邻居中 #将该节点放入邻居中 self.tablsp.neighbourip.append(seed_node.ip) self.tablsp.neighbourport.append(seed_node.port) #设置距离 d = random.choice((1, 2, 3, 4, 40, 40, 40, 40)) print "insert distance in bootrs" print d self.tablsp.neighbourdistance.append(d) # 握手,加入lsptab中 self.sendversion( seed_node, Version( 1, int(time.time()), self.node_id, seed_node.node_id, db.get_block_height( self.blockchain.get_wallet_address()), d)) #建立邻居完毕后整理lsp self.tablsp.generatelsp() print "neighbour" print self.tablsp.lsp
def node_info(): values = request.get_json() required = ['ip', 'port'] if not all(k in values for k in required): return 'Missing values', 400 ip = values['ip'] port = values['port'] block_height = db.get_block_height(blockchain.wallet.address) latest_block = db.get_block_data_by_index(blockchain.wallet.address, block_height - 1) block_hash = latest_block.current_hash timestamp = latest_block.timestamp time_local = time.localtime(timestamp) response = { 'address': ip + ':' + str(port), 'block_height': block_height, 'block_hash': block_hash, 'wallet_address': blockchain.wallet.address, # 'balance': blockchain.get_balance(blockchain.wallet.address), 'timestamp': time.strftime("%Y-%m-%d %H:%M:%S", time_local) } return jsonify(response), 200
def handle_verack(self, payload): version = payload.version if version != 1: # 版本不一样,拒绝 print '[Warn] invalid version, ignore!!' pass else: client_node_id = payload.from_id client_ip, client_port = self.client_address new_node = Node(client_ip, client_port, client_node_id) new_node.version = 1 self.server.node_manager.buckets.insert(new_node) blockchain = self.server.node_manager.blockchain if payload.best_height > db.get_block_height( blockchain.get_wallet_address()): # TODO 检查best_height,同步区块链 pass
def bootstrap(self, seed_nodes=[]): """ 根据初始节点引导初始化 :param seed_nodes:<Node list> 种子节点列表 :return: """ for seed_node in seed_nodes: # 握手 self.sendversion( seed_node, Version( 1, int(time.time()), self.node_id, seed_node.node_id, db.get_block_height(self.blockchain.get_wallet_address()))) for seed_node in seed_nodes: self.iterative_find_nodes(self.client.node_id, seed_node) if len(seed_nodes) == 0: for seed_node in self.buckets.get_all_nodes(): self.iterative_find_nodes(self.client.node_id, seed_node)
def handle_version(self, payload): version = payload.version if version != 1: # 版本不一样,拒绝 print '[Warn] invalid version, ignore!!' pass else: client_ip, client_port = self.client_address client_node_id = payload.from_id new_node = Node(client_ip, client_port, client_node_id) new_node.version = 1 self.server.node_manager.buckets.insert(new_node) blockchain = self.server.node_manager.blockchain block_counts = db.get_block_height(blockchain.get_wallet_address()) verack = Verack(1, int(time.time()), self.server.node_manager.node_id, client_node_id, block_counts) self.server.node_manager.sendverck(new_node, verack) if payload.best_height > block_counts: # TODO 检查best_height,同步区块链 pass
def find_transaction(self, txid): """ 通过交易id找到一笔Tx交易 :param txid: <str>交易id :return: """ # 在区块中寻找(已确认的交易) block_height = db.get_block_height(self.wallet.address) for index in range(block_height): block = db.get_block_data_by_index(self.wallet.address, index) # 1.获取区块下的所有的交易 transactions = block.get_transactions() for tx in transactions: if tx.txid == txid: return tx # 在交易池中寻找(未确认的交易)TODO待确认 for k in range(len(self.current_transactions)): uncomfirmed_tx = self.current_transactions[-1 - k] if uncomfirmed_tx.txid == txid: return uncomfirmed_tx return None
def get_last_block(self): block_height = db.get_block_height(self.wallet.address) return db.get_block_data_by_index(self.wallet.address, block_height - 1)
def find_spendalbe_outputs(self, from_addr): """ 获取from_addr可以用于交易的TxOutput(未使用过的), :param from_addr: <str>发送方钱包地址 :return: """ unspent_txout_list = list() spent_txout_list = list() balance = 0 # Step0:遍历交易池中已经发生过的交易(未打包进区块,未确认) # 备注:要从最新的交易开始遍历!!!!! for i in range(len(self.current_transactions)): unconfirmed_tx = self.current_transactions[ len(self.current_transactions) - 1 - i] txid = unconfirmed_tx.txid # 遍历当前交易下所有的TxInput if not unconfirmed_tx.is_coinbase(): # print 'txid:', txid # 记录当前tx下被from_addr被使用过的上一次交易的输出,即记录txid和out_idx for txin in unconfirmed_tx.txins: if txin.can_unlock_txoutput_with(from_addr): spent_txid = txin.prev_txid spent_tx_out_idx = txin.prev_tx_out_idx spent_txout_list.append((spent_txid, spent_tx_out_idx)) # 遍历交易下所有的未使用过的TxOutput for out_idx in range(len(unconfirmed_tx.txouts)): txout = unconfirmed_tx.txouts[out_idx] if not (txid, out_idx) in spent_txout_list: if txout.can_be_unlocked_with(from_addr): unspent_txout_list.append((txid, out_idx, txout)) # -------------------------------------------------- # Step1:获取from_addr下可以未使用过的TxOutput(打包在区块,已确认) block_height = db.get_block_height(self.wallet.address) for i in range(block_height): block = db.get_block_data_by_index(self.wallet.address, block_height - 1 - i) # 1.获取区块下的所有的交易 transactions = block.get_transactions() # 备注:要从最新的交易开始遍历!!!!! for k in range(len(transactions)): tx = transactions[len(transactions) - 1 - k] if not self.verify_transaction(tx): # 校验交易是否有效 print('[Info] invalid tx', tx.txid, 'block index:', i) continue txid = tx.txid # 当前交易的id # 2.遍历某个交易下所有的TxInput if not tx.is_coinbase(): # 记录当前tx下被from_addr被使用过的上一次交易的输出,即记录txid和out_idx for txin in tx.txins: if txin.can_unlock_txoutput_with(from_addr): spent_txid = txin.prev_txid spent_tx_out_idx = txin.prev_tx_out_idx spent_txout_list.append( (spent_txid, spent_tx_out_idx)) # 3.遍历某个交易下所有的未使用过的TxOutput for out_idx in range(len(tx.txouts)): txout = tx.txouts[out_idx] if not (txid, out_idx) in spent_txout_list: if txout.can_be_unlocked_with(from_addr): unspent_txout_list.append((txid, out_idx, txout)) # Step2:计算这些未使用过的TxOutput输出之和 for txid, out_idx, txout in unspent_txout_list: balance += txout.value return balance, unspent_txout_list
def get_balance_by_db(self, from_addr): """ 获取from_addr可以用于交易的TxOutput(未使用过的),读取交易池和区块链的本地副本,避免被加锁 :param from_addr: <str>发送方钱包地址 :return: <int> """ unspent_txout_list = list() spent_txout_list = list() balance = 0 # Step1:遍历交易池中已经发生过的交易(未打包进区块,未确认) # 备注:要从最新的交易开始遍历!!!!! current_transactions = db.get_all_unconfirmed_tx(self.wallet.address) current_transactions = sorted(current_transactions, key=lambda x: x.timestamp, reverse=False) for i in range(len(current_transactions)): unconfirmed_tx = current_transactions[len(current_transactions) - 1 - i] txid = unconfirmed_tx.txid # 遍历当前交易下所有的TxInput if not unconfirmed_tx.is_coinbase(): # print 'txid:', txid # 记录当前tx下被from_addr被使用过的上一次交易的输出,即记录txid和out_idx for txin in unconfirmed_tx.txins: if txin.can_unlock_txoutput_with(from_addr): spent_txid = txin.prev_txid spent_tx_out_idx = txin.prev_tx_out_idx spent_txout_list.append((spent_txid, spent_tx_out_idx)) # 遍历交易下所有的未使用过的TxOutput for out_idx in range(len(unconfirmed_tx.txouts)): txout = unconfirmed_tx.txouts[out_idx] if not (txid, out_idx) in spent_txout_list: if txout.can_be_unlocked_with(from_addr): unspent_txout_list.append((txid, out_idx, txout)) # -------------------------------------------------- # Step2:获取from_addr下可以未使用过的TxOutput(打包在区块,已确认) block_height = db.get_block_height(self.wallet.address) for i in range(block_height): block = db.get_block_data_by_index(self.wallet.address, block_height - 1 - i) # 1.获取区块下的所有的交易 transactions = block.get_transactions() # 备注:要从最新的交易开始遍历!!!!! for k in range(len(transactions)): tx = transactions[len(transactions) - 1 - k] if not self.verify_transaction(tx): # 校验交易是否有效 continue txid = tx.txid # 当前交易的id # 2.遍历某个交易下所有的TxInput if not tx.is_coinbase(): # 记录当前tx下被from_addr被使用过的上一次交易的输出,即记录txid和out_idx for txin in tx.txins: if txin.can_unlock_txoutput_with(from_addr): spent_txid = txin.prev_txid spent_tx_out_idx = txin.prev_tx_out_idx spent_txout_list.append( (spent_txid, spent_tx_out_idx)) # 3.遍历某个交易下所有的未使用过的TxOutput for out_idx in range(len(tx.txouts)): txout = tx.txouts[out_idx] if not (txid, out_idx) in spent_txout_list: if txout.can_be_unlocked_with(from_addr): unspent_txout_list.append((txid, out_idx, txout)) # Step2:计算这些未使用过的TxOutput输出之和 for txid, out_idx, txout in unspent_txout_list: balance += txout.value return balance
def block_height(): response = { 'code': 0, 'value': db.get_block_height(blockchain.wallet.address) } return json.dumps(response), 200