def assemble_and_solve_block(self, txns=None) -> Block: """ Construct a Block by pulling transactions from the mempool, then mine it. """ with self.chain_lock: #chain_use_id = [str(number).split('.')[0] + '.' + str(number).split('.')[1][:5] for number in [random.random()]][0] #logger.info(f'####### into chain_lock: {chain_use_id} of assemble_and_solve_block') prev_block_hash = self.active_chain.chain[ -1].id if self.active_chain.chain else None block = Block( version=0, prev_block_hash=prev_block_hash, merkle_hash='', timestamp=int(time.time()), bits=Block.get_next_work_required(prev_block_hash, self.active_chain, self.side_branches), nonce=0, txns=[None, *txns] if txns else [None], ) if block.bits is None: #logger.info(f'####### out of chain_lock: {chain_use_id} of assemble_and_solve_block') return None if not block.txns[1:]: block = self.mempool.select_from_mempool(block, self.utxo_set) # print("EdgenceChain 113: build block with txn:", end="") # print(block) if len(block.txns[1:]) > 0: logger.info( f'{len(block.txns[1:])} transactions selected from mempool to construct this block' ) # print("EdgenceChain 117: build txn:") # print(block) fees = block.calculate_fees(self.utxo_set) # print("fee is: ", end="") # print(fees) my_address = self.wallet()[2] coinbase_txn = Transaction.create_coinbase( my_address, Block.get_block_subsidy(self.active_chain) + fees, self.active_chain.height) #logger.info(f'####### out of chain_lock: {chain_use_id} of assemble_and_solve_block') block.txns[0] = coinbase_txn block = block._replace( merkle_hash=MerkleNode.get_merkle_root_of_txns(block.txns).val) if len(Utils.serialize(block)) > Params.MAX_BLOCK_SERIALIZED_SIZE: raise ValueError('txns specified create a block too large') block = PoW.mine(block, self.mine_interrupt) # print("EdgenceChain 138: after mine: ") # print(block) return block
def do_connect_block_and_after(cls, block: Block, chain_idx, active_chain: BlockChain, side_branches: Iterable[BlockChain], \ mempool: BaseMemPool, utxo_set: BaseUTXO_Set, mine_interrupt: threading.Event) -> bool: if int(chain_idx) == int(Params.ACTIVE_CHAIN_IDX): if block.block_subsidy_fees != Block.get_block_subsidy( active_chain) + block.calculate_fees(utxo_set): #logger.info(f'{block.block_subsidy_fees} != {Block.get_block_subsidy(active_chain)} + {block.calculate_fees(utxo_set)}') logger.info( f'[p2p] subsidy and fees of this block are not right, so discard this block and return.' ) #logger.info(f'after check subsid_fees, and give out a logger.exception') return False else: #logger.info(f'[p2p] subsidy and fees of this block are right.') pass connect_block_success = active_chain.connect_block(block, active_chain, \ side_branches, \ mempool, utxo_set, mine_interrupt) else: connect_block_success = side_branches[chain_idx-1].connect_block(block, \ active_chain, side_branches, \ mempool, utxo_set, mine_interrupt) if connect_block_success is not False: if connect_block_success is not True: # -1, success and reorg #logger.info(f'[p2p] a successful reorg is found, begin to deal with {len(side_branches)} side branches') for branch_chain in side_branches: #logger.info(f'[p2p] number of blocks before slim side branch: {len(branch_chain.chain)}') #TCPHandler.printBlockchainIDs(branch_chain, '[p2p] side branch removed from active chain ') fork_height_from_end = 0 for block in branch_chain.chain[::-1]: if not Block.locate_block(block.id, active_chain)[0]: if not Block.locate_block(block.prev_block_hash, active_chain)[0]: fork_height_from_end += 1 else: break else: branch_chain.chain = [] logger.info( f'[p2p] the whole body of this branch chain is in active chain' ) break if fork_height_from_end >= branch_chain.height and branch_chain.height != 0: branch_chain.chain = [] logger.info( f'[p2p] all blocks are orphans to the current active chain' ) else: for num_to_pop in range( 1, branch_chain.height - fork_height_from_end): branch_chain.chain.pop(0) #logger.info(f'[p2p] number of blocks after slim side branch: {len(branch_chain.chain)}') side_branches_to_discard = [] for branch_chain in side_branches: if branch_chain.chain == []: side_branches_to_discard.append(branch_chain) continue fork_block, fork_height, _ = Block.locate_block( branch_chain.chain[0].prev_block_hash, active_chain) if fork_block is None: side_branches_to_discard.append(branch_chain) branch_height_real = branch_chain.height + fork_height if active_chain.height - branch_height_real > Params.MAXIMUM_ALLOWABLE_HEIGHT_DIFF: side_branches_to_discard.append(branch_chain) if len(side_branches_to_discard) > 0: logger.info( f'[p2p] ## delete {len(side_branches_to_discard)} side branches beyond MAXIMUM_ALLOWABLE_HEIGHT_DIFF' ) for branch_chain in side_branches_to_discard: side_branches.remove(branch_chain) for index, branch_chain in enumerate(side_branches, 1): branch_chain.index = index else: logger.exception(f'[p2p] connect_block returned a False value') return True