async def test_wallet_coinbase_reorg(self, wallet_node): num_blocks = 10 full_nodes, wallets = wallet_node full_node_1, server_1 = full_nodes[0] wallet_node, server_2 = wallets[0] wallet = wallet_node.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() await server_2.start_client(PeerInfo("localhost", uint16(server_1._port)), None) for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) await asyncio.sleep(3) funds = sum( [ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, num_blocks - 2) ] ) assert await wallet.get_confirmed_balance() == funds await full_node_1.reorg_from_index_to_new_index( ReorgProtocol(uint32(5), uint32(num_blocks + 3), token_bytes()) ) await asyncio.sleep(3) funds = sum( [ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, 5) ] ) assert await wallet.get_confirmed_balance() == funds
async def update_data(self): try: while not self.closed: heads: List[HeaderBlock] = self.blockchain.get_current_tips() self.latest_blocks = await self.get_latest_blocks(heads) header_block = heads[0] coin_balances = { bytes(header_block.proof_of_space.pool_pubkey): calculate_block_reward(header_block.height) } while header_block.height != 0: header_block = self.blockchain.header_blocks[ header_block.prev_header_hash] pool_pk = bytes(header_block.proof_of_space.pool_pubkey) if pool_pk not in coin_balances: coin_balances[pool_pk] = 0 coin_balances[pool_pk] += calculate_block_reward( header_block.height) self.top_winners = sorted( [(rewards, key) for key, rewards in coin_balances.items()], reverse=True, )[:self.num_top_block_pools] self.our_winners = [ (coin_balances[bytes(pk)], bytes(pk)) if bytes(pk) in coin_balances else (0, bytes(pk)) for pk in self.pool_pks ] await asyncio.sleep(5) except concurrent.futures._base.CancelledError as e: log.warn(f"Cancelled error in UI: {type(e)}: {e}") except Exception as e: log.warn(f"Exception in UI update_data {type(e)}: {e}") raise e
async def test_wallet_make_transaction(self, two_wallet_nodes): num_blocks = 10 full_nodes, wallets = two_wallet_nodes full_node_1, server_1 = full_nodes[0] wallet_node, server_2 = wallets[0] wallet_node_2, server_3 = wallets[1] wallet = wallet_node.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() await server_2.start_client(PeerInfo("localhost", uint16(server_1._port)), None) for i in range(0, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) funds = sum( [ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(0, num_blocks - 2) ] ) await asyncio.sleep(2) assert await wallet.get_confirmed_balance() == funds assert await wallet.get_unconfirmed_balance() == funds spend_bundle = await wallet.generate_signed_transaction( 10, await wallet_node_2.wallet_state_manager.main_wallet.get_new_puzzlehash(), 0, ) await wallet.push_transaction(spend_bundle) await asyncio.sleep(2) confirmed_balance = await wallet.get_confirmed_balance() unconfirmed_balance = await wallet.get_unconfirmed_balance() assert confirmed_balance == funds assert unconfirmed_balance == funds - 10 for i in range(0, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) await asyncio.sleep(2) new_funds = sum( [ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(0, (2 * num_blocks) - 2) ] ) confirmed_balance = await wallet.get_confirmed_balance() unconfirmed_balance = await wallet.get_unconfirmed_balance() assert confirmed_balance == new_funds - 10 assert unconfirmed_balance == new_funds - 10
async def test_colour_creation(self, two_wallet_nodes): num_blocks = 5 full_nodes, wallets = two_wallet_nodes full_node_1, server_1 = full_nodes[0] wallet_node, server_2 = wallets[0] wallet = wallet_node.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() await server_2.start_client(PeerInfo("localhost", uint16(server_1._port)), None) for i in range(1, 4): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) funds = sum( [ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, 4 - 2) ] ) await time_out_assert(15, wallet.get_confirmed_balance, funds) cc_wallet: CCWallet = await CCWallet.create_new_cc( wallet_node.wallet_state_manager, wallet, uint64(100) ) for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) await time_out_assert(15, cc_wallet.get_confirmed_balance, 100) await time_out_assert(15, cc_wallet.get_unconfirmed_balance, 100)
async def proof_of_space_finalized( self, proof_of_space_finalized: farmer_protocol.ProofOfSpaceFinalized): """ Full node notifies farmer that a proof of space has been completed. It gets added to the challenges list at that weight, and weight is updated if necessary """ get_proofs: bool = False if (proof_of_space_finalized.weight >= self.current_weight and proof_of_space_finalized.challenge_hash not in self.seen_challenges): # Only get proofs for new challenges, at a current or new weight get_proofs = True if proof_of_space_finalized.weight > self.current_weight: self.current_weight = proof_of_space_finalized.weight # TODO: ask the pool for this information coinbase: CoinbaseInfo = CoinbaseInfo( uint32(proof_of_space_finalized.height + 1), calculate_block_reward(proof_of_space_finalized.height), bytes.fromhex(self.key_config["pool_target"]), ) pool_sks: List[PrivateKey] = [ PrivateKey.from_bytes(bytes.fromhex(ce)) for ce in self.key_config["pool_sks"] ] coinbase_signature: PrependSignature = pool_sks[0].sign_prepend( bytes(coinbase)) self.coinbase_rewards[uint32(proof_of_space_finalized.height + 1)] = ( coinbase, coinbase_signature, ) log.info(f"\tCurrent weight set to {self.current_weight}") self.seen_challenges.add(proof_of_space_finalized.challenge_hash) if proof_of_space_finalized.weight not in self.challenges: self.challenges[proof_of_space_finalized.weight] = [ proof_of_space_finalized ] else: self.challenges[proof_of_space_finalized.weight].append( proof_of_space_finalized) self.challenge_to_weight[ proof_of_space_finalized. challenge_hash] = proof_of_space_finalized.weight self.challenge_to_height[ proof_of_space_finalized. challenge_hash] = proof_of_space_finalized.height if get_proofs: message = harvester_protocol.NewChallenge( proof_of_space_finalized.challenge_hash) yield OutboundMessage( NodeType.HARVESTER, Message("new_challenge", message), Delivery.BROADCAST, )
async def test_generate_zero_val(self, two_wallet_nodes): num_blocks = 10 full_nodes, wallets = two_wallet_nodes full_node_1, server_1 = full_nodes[0] wallet_node, server_2 = wallets[0] wallet_node_2, server_3 = wallets[1] wallet = wallet_node.wallet_state_manager.main_wallet wallet2 = wallet_node_2.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() await server_2.start_client(PeerInfo("localhost", uint16(server_1._port)), None) await server_3.start_client(PeerInfo("localhost", uint16(server_1._port)), None) for i in range(1, 4): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) funds = sum( [ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, 4 - 2) ] ) await time_out_assert(15, wallet.get_confirmed_balance, funds) cc_wallet: CCWallet = await CCWallet.create_new_cc( wallet_node.wallet_state_manager, wallet, uint64(100) ) ph = await wallet2.get_new_puzzlehash() for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) await time_out_assert(15, cc_wallet.get_confirmed_balance, 100) await time_out_assert(15, cc_wallet.get_unconfirmed_balance, 100) assert cc_wallet.cc_info.my_genesis_checker is not None colour = cc_wallet.get_colour() cc_wallet_2: CCWallet = await CCWallet.create_wallet_for_cc( wallet_node_2.wallet_state_manager, wallet2, colour ) assert ( cc_wallet.cc_info.my_genesis_checker == cc_wallet_2.cc_info.my_genesis_checker ) await cc_wallet_2.generate_zero_val_coin() for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) unspent: List[WalletCoinRecord] = list( await cc_wallet_2.wallet_state_manager.get_spendable_coins_for_wallet( cc_wallet_2.id() ) ) assert len(unspent) == 1 assert unspent.pop().coin.amount == 0
async def test_coin_backup(self, two_wallet_nodes): num_blocks = 5 full_nodes, wallets = two_wallet_nodes full_node_1, server_1 = full_nodes[0] wallet_node, server_2 = wallets[0] wallet = wallet_node.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) for i in range(1, 4): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) funds = sum([ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, 4 - 2) ]) await time_out_assert(15, wallet.get_confirmed_balance, funds) cc_wallet: CCWallet = await CCWallet.create_new_cc( wallet_node.wallet_state_manager, wallet, uint64(100)) for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) await time_out_assert(15, cc_wallet.get_confirmed_balance, 100) await time_out_assert(15, cc_wallet.get_unconfirmed_balance, 100) # Write backup to file filename = f"test-backup-{token_bytes(16).hex()}" file_path = Path(filename) await wallet_node.wallet_state_manager.create_wallet_backup(file_path) # Close wallet and restart db_path = wallet_node.wallet_state_manager.db_path wallet_node._close() await wallet_node._await_closed() db_path.unlink() started = await wallet_node._start() assert started is False await wallet_node._start(backup_file=file_path) await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) all_wallets = wallet_node.wallet_state_manager.wallets assert len(all_wallets) == 2 cc_wallet_from_backup = wallet_node.wallet_state_manager.wallets[2] await time_out_assert(15, cc_wallet_from_backup.get_confirmed_balance, 100) if file_path.exists(): file_path.unlink()
async def test_wallet_make_transaction(self, two_wallet_nodes): num_blocks = 5 full_nodes, wallets = two_wallet_nodes full_node_1, server_1 = full_nodes[0] wallet_node, server_2 = wallets[0] wallet_node_2, server_3 = wallets[1] wallet = wallet_node.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) for i in range(0, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) funds = sum([ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, num_blocks - 1) ]) await time_out_assert(5, wallet.get_confirmed_balance, funds) await time_out_assert(5, wallet.get_unconfirmed_balance, funds) tx = await wallet.generate_signed_transaction( 10, await wallet_node_2.wallet_state_manager.main_wallet.get_new_puzzlehash( ), 0, ) await wallet.push_transaction(tx) await time_out_assert(5, wallet.get_confirmed_balance, funds) await time_out_assert(5, wallet.get_unconfirmed_balance, funds - 10) for i in range(0, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) new_funds = sum([ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, (2 * num_blocks) - 1) ]) await time_out_assert(5, wallet.get_confirmed_balance, new_funds - 10) await time_out_assert(5, wallet.get_unconfirmed_balance, new_funds - 10)
async def get_pool_balances(self, request) -> web.Response: """ Retrieves the coinbase balances earned by all pools. TODO: remove after transactions and coins are added. """ tips: List[HeaderBlock] = self.full_node.blockchain.get_current_tips() header_block = tips[0] coin_balances: Dict[str, uint64] = { f"0x{bytes(header_block.proof_of_space.pool_pubkey).hex()}": calculate_block_reward(header_block.height) } while header_block.height != 0: header_block = self.full_node.blockchain.header_blocks[ header_block.prev_header_hash] pool_pk = f"0x{bytes(header_block.proof_of_space.pool_pubkey).hex()}" if pool_pk not in coin_balances: coin_balances[pool_pk] = uint64(0) coin_balances[pool_pk] = uint64( coin_balances[pool_pk] + calculate_block_reward(header_block.height)) return obj_to_response(coin_balances)
async def test_wallet_coinbase(self, wallet_node): num_blocks = 5 full_nodes, wallets = wallet_node full_node_1, server_1 = full_nodes[0] wallet_node, server_2 = wallets[0] wallet = wallet_node.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) funds = sum([ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, num_blocks - 2) ]) await time_out_assert(5, wallet.get_confirmed_balance, funds)
async def get_pool_balances(self, request) -> web.Response: """ Retrieves the coinbase balances earned by all pools. TODO: remove after transactions and coins are added. """ ppks: List[ Tuple[uint32, PublicKey]] = await self.full_node.store.get_pool_pks_hack() coin_balances: Dict[str, uint64] = {} for height, pk in ppks: pool_pk = f"0x{bytes(pk).hex()}" if pool_pk not in coin_balances: coin_balances[pool_pk] = uint64(0) coin_balances[pool_pk] = uint64(coin_balances[pool_pk] + calculate_block_reward(height)) return obj_to_response(coin_balances)
def pre_validate_block_multi(data) -> Tuple[bool, Optional[bytes]]: """ Validates all parts of FullBlock that don't need to be serially checked """ block = FullBlock.from_bytes(data) if not block.header_block.challenge or not block.header_block.proof_of_time: return False, None if ( block.header_block.proof_of_space.get_hash() != block.header_block.challenge.proof_of_space_hash ): return False, None # 4. Check PoT if not block.header_block.proof_of_time.is_valid( consensus_constants["DISCRIMINANT_SIZE_BITS"] ): return False, None if block.body.coinbase.height != block.header_block.challenge.height: return False, None if ( calculate_block_reward(block.body.coinbase.height) != block.body.coinbase.amount ): return False, None # 9. Check harvester signature of header data is valid based on harvester key if not block.header_block.header.harvester_signature.verify( [blspy.Util.hash256(block.header_block.header.data.get_hash())], [block.header_block.proof_of_space.plot_pubkey], ): return False, None # 10. Check proof of space based on challenge pos_quality = block.header_block.proof_of_space.verify_and_get_quality() if not pos_quality: return False, None return True, bytes(pos_quality)
async def test_wallet_make_transaction_hop(self, two_wallet_nodes_five_freeze): num_blocks = 10 full_nodes, wallets = two_wallet_nodes_five_freeze full_node_0, full_node_server = full_nodes[0] wallet_node_0, wallet_0_server = wallets[0] wallet_node_1, wallet_1_server = wallets[1] wallet_0 = wallet_node_0.wallet_state_manager.main_wallet wallet_1 = wallet_node_1.wallet_state_manager.main_wallet ph = await wallet_0.get_new_puzzlehash() await wallet_0_server.start_client( PeerInfo("localhost", uint16(full_node_server._port)), None) await wallet_1_server.start_client( PeerInfo("localhost", uint16(full_node_server._port)), None) for i in range(0, num_blocks): await full_node_0.farm_new_block(FarmNewBlockProtocol(ph)) funds = sum([ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, num_blocks - 1) ]) await time_out_assert(5, wallet_0.get_confirmed_balance, funds) await time_out_assert(5, wallet_0.get_unconfirmed_balance, funds) assert await wallet_0.get_confirmed_balance() == funds assert await wallet_0.get_unconfirmed_balance() == funds tx = await wallet_0.generate_signed_transaction( 10, await wallet_node_1.wallet_state_manager.main_wallet.get_new_puzzlehash( ), 0, ) await wallet_0.push_transaction(tx) # Full node height 11, wallet height 9 await time_out_assert(5, wallet_0.get_confirmed_balance, funds) await time_out_assert(5, wallet_0.get_unconfirmed_balance, funds - 10) for i in range(0, 4): await full_node_0.farm_new_block( FarmNewBlockProtocol(token_bytes())) new_funds = sum([ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, num_blocks + 1) ]) # Full node height 17, wallet height 15 await time_out_assert(5, wallet_0.get_confirmed_balance, new_funds - 10) await time_out_assert(5, wallet_0.get_unconfirmed_balance, new_funds - 10) await time_out_assert(5, wallet_1.get_confirmed_balance, 10) tx = await wallet_1.generate_signed_transaction( 5, await wallet_0.get_new_puzzlehash(), 0) await wallet_1.push_transaction(tx) for i in range(0, 4): await full_node_0.farm_new_block( FarmNewBlockProtocol(token_bytes())) await wallet_0.get_confirmed_balance() await wallet_0.get_unconfirmed_balance() await wallet_1.get_confirmed_balance() await time_out_assert(5, wallet_0.get_confirmed_balance, new_funds - 5) await time_out_assert(5, wallet_0.get_unconfirmed_balance, new_funds - 5) await time_out_assert(5, wallet_1.get_confirmed_balance, 5)
async def test_cc_spend(self, two_wallet_nodes): num_blocks = 8 full_nodes, wallets = two_wallet_nodes full_node_1, server_1 = full_nodes[0] wallet_node, server_2 = wallets[0] wallet_node_2, server_3 = wallets[1] wallet = wallet_node.wallet_state_manager.main_wallet wallet2 = wallet_node_2.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() await server_2.start_client(PeerInfo("localhost", uint16(server_1._port)), None) await server_3.start_client(PeerInfo("localhost", uint16(server_1._port)), None) for i in range(1, 4): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) funds = sum( [ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, 4 - 2) ] ) await time_out_assert(15, wallet.get_confirmed_balance, funds) cc_wallet: CCWallet = await CCWallet.create_new_cc( wallet_node.wallet_state_manager, wallet, uint64(100) ) for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) await time_out_assert(15, cc_wallet.get_confirmed_balance, 100) await time_out_assert(15, cc_wallet.get_unconfirmed_balance, 100) assert cc_wallet.cc_info.my_core is not None colour = cc_wallet_puzzles.get_genesis_from_core(cc_wallet.cc_info.my_core) cc_wallet_2: CCWallet = await CCWallet.create_wallet_for_cc( wallet_node_2.wallet_state_manager, wallet2, colour ) assert cc_wallet.cc_info.my_core == cc_wallet_2.cc_info.my_core cc_2_hash = await cc_wallet_2.get_new_inner_hash() tx_record = await cc_wallet.generate_signed_transaction(uint64(60), cc_2_hash) await wallet.wallet_state_manager.add_pending_transaction(tx_record) for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) await time_out_assert(15, cc_wallet.get_confirmed_balance, 40) await time_out_assert(15, cc_wallet.get_unconfirmed_balance, 40) await time_out_assert(30, cc_wallet_2.get_confirmed_balance, 60) await time_out_assert(30, cc_wallet_2.get_unconfirmed_balance, 60) cc_hash = await cc_wallet.get_new_inner_hash() tx_record = await cc_wallet_2.generate_signed_transaction(uint64(15), cc_hash) await wallet.wallet_state_manager.add_pending_transaction(tx_record) for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) await time_out_assert(15, cc_wallet.get_confirmed_balance, 55) await time_out_assert(15, cc_wallet.get_unconfirmed_balance, 55)
def get_coinbase(self) -> Coin: br = calculate_block_reward(self.height) return create_coinbase_coin(self.height, self.header.data.pool_target.puzzle_hash, br)
async def test_wallet_make_transaction_with_fee(self, two_wallet_nodes): num_blocks = 10 full_nodes, wallets = two_wallet_nodes full_node_1, server_1 = full_nodes[0] wallet_node, server_2 = wallets[0] wallet_node_2, server_3 = wallets[1] wallet = wallet_node.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) for i in range(0, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) funds = sum([ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, num_blocks - 1) ]) await asyncio.sleep(2) assert await wallet.get_confirmed_balance() == funds assert await wallet.get_unconfirmed_balance() == funds tx_amount = 32000000000000 tx_fee = 10 tx = await wallet.generate_signed_transaction( tx_amount, await wallet_node_2.wallet_state_manager.main_wallet.get_new_puzzlehash( ), tx_fee, ) fees = tx.spend_bundle.fees() assert fees == tx_fee await wallet.push_transaction(tx) await asyncio.sleep(2) confirmed_balance = await wallet.get_confirmed_balance() unconfirmed_balance = await wallet.get_unconfirmed_balance() assert confirmed_balance == funds assert unconfirmed_balance == funds - tx_amount - tx_fee for i in range(0, num_blocks): await full_node_1.farm_new_block( FarmNewBlockProtocol(token_bytes())) await asyncio.sleep(2) new_funds = sum([ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, num_blocks + 1) ]) confirmed_balance = await wallet.get_confirmed_balance() unconfirmed_balance = await wallet.get_unconfirmed_balance() assert confirmed_balance == new_funds - tx_amount - tx_fee assert unconfirmed_balance == new_funds - tx_amount - tx_fee
async def validate_unfinished_block( self, block: FullBlock, genesis: bool = False, pre_validated: bool = True, pos_quality: bytes32 = None, ) -> bool: """ Block validation algorithm. Returns true if the candidate block is fully valid (except for proof of time). The same as validate_block, but without proof of time and challenge validation. """ if not pre_validated: # 1. Check the proof of space hash is valid if ( block.header_block.proof_of_space.get_hash() != block.header_block.header.data.proof_of_space_hash ): return False # 2. Check body hash if block.body.get_hash() != block.header_block.header.data.body_hash: return False # 3. Check coinbase amount if ( calculate_block_reward(block.body.coinbase.height) != block.body.coinbase.amount ): return False # 4. Check coinbase signature with pool pk if not block.body.coinbase_signature.verify( [blspy.Util.hash256(bytes(block.body.coinbase))], [block.header_block.proof_of_space.pool_pubkey], ): return False # 5. Check harvester signature of header data is valid based on harvester key if not block.header_block.header.harvester_signature.verify( [blspy.Util.hash256(block.header_block.header.data.get_hash())], [block.header_block.proof_of_space.plot_pubkey], ): return False # 6. Check previous pointer(s) / flyclient if not genesis and block.prev_header_hash not in self.header_blocks: return False # 7. Check Now+2hrs > timestamp > avg timestamp of last 11 blocks prev_block: Optional[HeaderBlock] = None if not genesis: # TODO: do something about first 11 blocks last_timestamps: List[uint64] = [] prev_block = self.header_blocks.get(block.prev_header_hash, None) if not prev_block: return False curr = prev_block while len(last_timestamps) < self.constants["NUMBER_OF_TIMESTAMPS"]: last_timestamps.append(curr.header.data.timestamp) fetched = self.header_blocks.get(curr.prev_header_hash, None) if not fetched: break curr = fetched if ( len(last_timestamps) != self.constants["NUMBER_OF_TIMESTAMPS"] and curr.height != 0 ): return False prev_time: uint64 = uint64(int(sum(last_timestamps) / len(last_timestamps))) if block.header_block.header.data.timestamp < prev_time: return False if ( block.header_block.header.data.timestamp > time.time() + self.constants["MAX_FUTURE_TIME"] ): return False # 8. Check filter hash is correct TODO # 9. Check extension data, if any is added # 10. Compute challenge of parent challenge_hash: bytes32 if not genesis: assert prev_block assert prev_block.challenge challenge_hash = prev_block.challenge.get_hash() # 8. Check challenge hash of prev is the same as in pos if challenge_hash != block.header_block.proof_of_space.challenge_hash: return False else: assert block.header_block.proof_of_time challenge_hash = block.header_block.proof_of_time.challenge_hash if challenge_hash != block.header_block.proof_of_space.challenge_hash: return False # 11. Check proof of space based on challenge if pos_quality is None: pos_quality = block.header_block.proof_of_space.verify_and_get_quality() if not pos_quality: return False # 12. Check coinbase height = prev height + 1 if not genesis: assert prev_block if block.body.coinbase.height != prev_block.height + 1: return False else: if block.body.coinbase.height != 0: return False # TODO: 14a. check transactions # TODO: 14b. Aggregate transaction results into signature if block.body.aggregated_signature: # TODO: 15. check that aggregate signature is valid, based on pubkeys, and messages pass # TODO: 16. check fees # TODO: 17. check cost return True
async def test_cc_spend_uncoloured(self, two_wallet_nodes): num_blocks = 10 full_nodes, wallets = two_wallet_nodes full_node_1, server_1 = full_nodes[0] wallet_node, server_2 = wallets[0] wallet_node_2, server_3 = wallets[1] wallet = wallet_node.wallet_state_manager.main_wallet wallet2 = wallet_node_2.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) await server_3.start_client( PeerInfo("localhost", uint16(server_1._port)), None) for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) funds = sum([ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, num_blocks - 2) ]) await self.time_out_assert(15, wallet.get_confirmed_balance, funds) cc_wallet: CCWallet = await CCWallet.create_new_cc( wallet_node.wallet_state_manager, wallet, uint64(100)) for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) await self.time_out_assert(15, cc_wallet.get_confirmed_balance, 100) await self.time_out_assert(15, cc_wallet.get_unconfirmed_balance, 100) assert cc_wallet.cc_info.my_core is not None colour = cc_wallet_puzzles.get_genesis_from_core( cc_wallet.cc_info.my_core) cc_wallet_2: CCWallet = await CCWallet.create_wallet_for_cc( wallet_node_2.wallet_state_manager, wallet2, colour) assert cc_wallet.cc_info.my_core == cc_wallet_2.cc_info.my_core cc_2_hash = await cc_wallet_2.get_new_inner_hash() await cc_wallet.cc_spend(uint64(60), cc_2_hash) for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) await self.time_out_assert(15, cc_wallet.get_confirmed_balance, 40) await self.time_out_assert(15, cc_wallet.get_unconfirmed_balance, 40) await self.time_out_assert(15, cc_wallet_2.get_confirmed_balance, 60) await self.time_out_assert(15, cc_wallet_2.get_unconfirmed_balance, 60) cc2_ph = await cc_wallet_2.get_new_cc_puzzle_hash() spend_bundle = await wallet.wallet_state_manager.main_wallet.generate_signed_transaction( 10, cc2_ph, 0) await wallet.wallet_state_manager.main_wallet.push_transaction( spend_bundle) for i in range(0, num_blocks): await full_node_1.farm_new_block( FarmNewBlockProtocol(token_bytes())) id = cc_wallet_2.wallet_info.id wsm = cc_wallet_2.wallet_state_manager await self.time_out_assert(15, wsm.get_confirmed_balance_for_wallet, 70, id) await self.time_out_assert(15, cc_wallet_2.get_confirmed_balance, 60) await self.time_out_assert(15, cc_wallet_2.get_unconfirmed_balance, 60)
async def test_cc_spend_multiple(self, three_wallet_nodes): num_blocks = 8 full_nodes, wallets = three_wallet_nodes full_node_1, server_1 = full_nodes[0] wallet_node_0, wallet_server_0 = wallets[0] wallet_node_1, wallet_server_1 = wallets[1] wallet_node_2, wallet_server_2 = wallets[2] wallet_0 = wallet_node_0.wallet_state_manager.main_wallet wallet_1 = wallet_node_1.wallet_state_manager.main_wallet wallet_2 = wallet_node_2.wallet_state_manager.main_wallet ph = await wallet_0.get_new_puzzlehash() await wallet_server_0.start_client( PeerInfo("localhost", uint16(server_1._port)), None ) await wallet_server_1.start_client( PeerInfo("localhost", uint16(server_1._port)), None ) await wallet_server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None ) for i in range(1, 4): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) funds = sum( [ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, 4 - 2) ] ) await time_out_assert(15, wallet_0.get_confirmed_balance, funds) cc_wallet_0: CCWallet = await CCWallet.create_new_cc( wallet_node_0.wallet_state_manager, wallet_0, uint64(100) ) for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) await time_out_assert(15, cc_wallet_0.get_confirmed_balance, 100) await time_out_assert(15, cc_wallet_0.get_unconfirmed_balance, 100) assert cc_wallet_0.cc_info.my_genesis_checker is not None colour = cc_wallet_0.get_colour() cc_wallet_1: CCWallet = await CCWallet.create_wallet_for_cc( wallet_node_1.wallet_state_manager, wallet_1, colour ) cc_wallet_2: CCWallet = await CCWallet.create_wallet_for_cc( wallet_node_2.wallet_state_manager, wallet_2, colour ) assert ( cc_wallet_0.cc_info.my_genesis_checker == cc_wallet_1.cc_info.my_genesis_checker ) assert ( cc_wallet_0.cc_info.my_genesis_checker == cc_wallet_2.cc_info.my_genesis_checker ) cc_1_hash = await cc_wallet_1.get_new_inner_hash() cc_2_hash = await cc_wallet_2.get_new_inner_hash() tx_record = await cc_wallet_0.generate_signed_transaction( [uint64(60), uint64(20)], [cc_1_hash, cc_2_hash] ) await wallet_0.wallet_state_manager.add_pending_transaction(tx_record) for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) await time_out_assert(15, cc_wallet_0.get_confirmed_balance, 20) await time_out_assert(15, cc_wallet_0.get_unconfirmed_balance, 20) await time_out_assert(30, cc_wallet_1.get_confirmed_balance, 60) await time_out_assert(30, cc_wallet_1.get_unconfirmed_balance, 60) await time_out_assert(30, cc_wallet_2.get_confirmed_balance, 20) await time_out_assert(30, cc_wallet_2.get_unconfirmed_balance, 20) cc_hash = await cc_wallet_0.get_new_inner_hash() tx_record = await cc_wallet_1.generate_signed_transaction( [uint64(15)], [cc_hash] ) await wallet_1.wallet_state_manager.add_pending_transaction(tx_record) tx_record_2 = await cc_wallet_2.generate_signed_transaction( [uint64(20)], [cc_hash] ) await wallet_2.wallet_state_manager.add_pending_transaction(tx_record_2) for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) await time_out_assert(15, cc_wallet_0.get_confirmed_balance, 55) await time_out_assert(15, cc_wallet_0.get_unconfirmed_balance, 55) await time_out_assert(30, cc_wallet_1.get_confirmed_balance, 45) await time_out_assert(30, cc_wallet_1.get_unconfirmed_balance, 45) await time_out_assert(30, cc_wallet_2.get_confirmed_balance, 0) await time_out_assert(30, cc_wallet_2.get_unconfirmed_balance, 0)
async def test_wallet_send_to_three_peers(self, three_sim_two_wallets): num_blocks = 10 full_nodes, wallets = three_sim_two_wallets wallet_0, wallet_server_0 = wallets[0] full_node_0, server_0 = full_nodes[0] full_node_1, server_1 = full_nodes[1] full_node_2, server_2 = full_nodes[2] ph = await wallet_0.wallet_state_manager.main_wallet.get_new_puzzlehash() # wallet0 <-> sever0 await wallet_server_0.start_client( PeerInfo("localhost", uint16(server_0._port)), None ) for i in range(1, num_blocks): await full_node_0.farm_new_block(FarmNewBlockProtocol(ph)) all_blocks = await full_node_0.get_current_blocks(full_node_0.get_tip()) for block in all_blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block) ): pass async for _ in full_node_2.respond_block( full_node_protocol.RespondBlock(block) ): pass await asyncio.sleep(2) funds = sum( [ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, num_blocks - 2) ] ) assert ( await wallet_0.wallet_state_manager.main_wallet.get_confirmed_balance() == funds ) spend_bundle = await wallet_0.wallet_state_manager.main_wallet.generate_signed_transaction( 10, token_bytes(), 0 ) await wallet_0.wallet_state_manager.main_wallet.push_transaction(spend_bundle) await asyncio.sleep(1) bundle0 = full_node_0.mempool_manager.get_spendbundle(spend_bundle.name()) assert bundle0 is not None # wallet0 <-> sever1 await wallet_server_0.start_client( PeerInfo("localhost", uint16(server_1._port)), wallet_0._on_connect ) await asyncio.sleep(1) bundle1 = full_node_1.mempool_manager.get_spendbundle(spend_bundle.name()) assert bundle1 is not None # wallet0 <-> sever2 await wallet_server_0.start_client( PeerInfo("localhost", uint16(server_2._port)), wallet_0._on_connect ) await asyncio.sleep(1) bundle2 = full_node_2.mempool_manager.get_spendbundle(spend_bundle.name()) assert bundle2 is not None
async def test_wallet_make_transaction_hop(self, two_wallet_nodes_five_freeze): num_blocks = 10 full_nodes, wallets = two_wallet_nodes_five_freeze full_node_0, full_node_server = full_nodes[0] wallet_node_0, wallet_0_server = wallets[0] wallet_node_1, wallet_1_server = wallets[1] wallet_0 = wallet_node_0.wallet_state_manager.main_wallet wallet_1 = wallet_node_1.wallet_state_manager.main_wallet ph = await wallet_0.get_new_puzzlehash() await wallet_0_server.start_client( PeerInfo("localhost", uint16(full_node_server._port)), None ) await wallet_1_server.start_client( PeerInfo("localhost", uint16(full_node_server._port)), None ) for i in range(0, num_blocks): await full_node_0.farm_new_block(FarmNewBlockProtocol(ph)) funds = sum( [ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(0, num_blocks - 2) ] ) await asyncio.sleep(2) assert await wallet_0.get_confirmed_balance() == funds assert await wallet_0.get_unconfirmed_balance() == funds spend_bundle = await wallet_0.generate_signed_transaction( 10, await wallet_node_1.wallet_state_manager.main_wallet.get_new_puzzlehash(), 0, ) await wallet_0.push_transaction(spend_bundle) await asyncio.sleep(1) # Full node height 11, wallet height 9 confirmed_balance = await wallet_0.get_confirmed_balance() unconfirmed_balance = await wallet_0.get_unconfirmed_balance() assert confirmed_balance == funds assert unconfirmed_balance == funds - 10 for i in range(0, 7): await full_node_0.farm_new_block(FarmNewBlockProtocol(token_bytes())) await asyncio.sleep(1) new_funds = sum( [ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(0, num_blocks) ] ) # Full node height 17, wallet height 15 confirmed_balance = await wallet_0.get_confirmed_balance() unconfirmed_balance = await wallet_0.get_unconfirmed_balance() wallet_2_confirmed_balance = await wallet_1.get_confirmed_balance() assert confirmed_balance == new_funds - 10 assert unconfirmed_balance == new_funds - 10 assert wallet_2_confirmed_balance == 10 spend_bundle = await wallet_1.generate_signed_transaction( 5, await wallet_0.get_new_puzzlehash(), 0 ) await wallet_1.push_transaction(spend_bundle) for i in range(0, 7): await full_node_0.farm_new_block(FarmNewBlockProtocol(token_bytes())) await asyncio.sleep(1) confirmed_balance = await wallet_0.get_confirmed_balance() unconfirmed_balance = await wallet_0.get_unconfirmed_balance() wallet_2_confirmed_balance = await wallet_1.get_confirmed_balance() assert confirmed_balance == new_funds - 5 assert unconfirmed_balance == new_funds - 5 assert wallet_2_confirmed_balance == 5
async def test_create_offer_with_zero_val(self, two_wallet_nodes): num_blocks = 10 full_nodes, wallets = two_wallet_nodes full_node_1, server_1 = full_nodes[0] wallet_node, server_2 = wallets[0] wallet_node_2, server_3 = wallets[1] wallet = wallet_node.wallet_state_manager.main_wallet wallet2 = wallet_node_2.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() ph2 = await wallet2.get_new_puzzlehash() await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) await server_3.start_client( PeerInfo("localhost", uint16(server_1._port)), None) for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) funds = sum([ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, num_blocks - 2) ]) await self.time_out_assert(15, wallet.get_confirmed_balance, funds) cc_wallet: CCWallet = await CCWallet.create_new_cc( wallet_node.wallet_state_manager, wallet, uint64(100)) for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) await self.time_out_assert(15, cc_wallet.get_unconfirmed_balance, 100) await self.time_out_assert(15, cc_wallet.get_confirmed_balance, 100) assert cc_wallet.cc_info.my_core is not None colour = cc_wallet_puzzles.get_genesis_from_core( cc_wallet.cc_info.my_core) cc_wallet_2: CCWallet = await CCWallet.create_wallet_for_cc( wallet_node_2.wallet_state_manager, wallet2, colour) assert cc_wallet.cc_info.my_core == cc_wallet_2.cc_info.my_core await full_node_1.farm_new_block(FarmNewBlockProtocol(ph2)) for i in range(1, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) trade_manager_1 = await TradeManager.create( wallet_node.wallet_state_manager) trade_manager_2 = await TradeManager.create( wallet_node_2.wallet_state_manager) file = "test_offer_file.offer" file_path = Path(file) if file_path.exists(): file_path.unlink() offer_dict = {1: -10, 2: 30} success, spend_bundle, error = await trade_manager_2.create_offer_for_ids( offer_dict) assert success is True assert spend_bundle is not None trade_manager_2.write_offer_to_disk(file_path, spend_bundle) success, offer, error = await trade_manager_1.get_discrepancies_for_offer( file_path) assert error is None assert success is True assert offer is not None assert offer["chia"] == 10 assert offer[colour] == -30 success, reason = await trade_manager_1.respond_to_offer(file_path) assert success is True for i in range(0, num_blocks): await full_node_1.farm_new_block( FarmNewBlockProtocol(token_bytes())) await self.time_out_assert(15, cc_wallet_2.get_confirmed_balance, 30) await self.time_out_assert(15, cc_wallet_2.get_confirmed_balance, 30)
async def test_tx_propagation(self, three_nodes_two_wallets): num_blocks = 5 full_nodes, wallets = three_nodes_two_wallets wallet_0, wallet_server_0 = wallets[0] wallet_1, wallet_server_1 = wallets[1] full_node_0, server_0 = full_nodes[0] full_node_1, server_1 = full_nodes[1] full_node_2, server_2 = full_nodes[2] ph = await wallet_0.wallet_state_manager.main_wallet.get_new_puzzlehash( ) ph1 = await wallet_1.wallet_state_manager.main_wallet.get_new_puzzlehash( ) # # wallet0 <-> sever0 <-> server1 <-> server2 <-> wallet1 # await wallet_server_0.start_client( PeerInfo("localhost", uint16(server_0._port)), None) await server_0.start_client( PeerInfo("localhost", uint16(server_1._port)), None) await server_1.start_client( PeerInfo("localhost", uint16(server_2._port)), None) await wallet_server_1.start_client( PeerInfo("localhost", uint16(server_2._port)), None) for i in range(1, num_blocks): await full_node_0.farm_new_block(FarmNewBlockProtocol(ph)) funds = sum([ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, num_blocks - 2) ]) await time_out_assert( 10, wallet_0.wallet_state_manager.main_wallet.get_confirmed_balance, funds) tx = (await wallet_0.wallet_state_manager.main_wallet. generate_signed_transaction(10, ph1, 0)) await wallet_0.wallet_state_manager.main_wallet.push_transaction(tx) await time_out_assert(10, full_node_0.mempool_manager.get_spendbundle, tx.spend_bundle, tx.name()) await time_out_assert(10, full_node_1.mempool_manager.get_spendbundle, tx.spend_bundle, tx.name()) await time_out_assert(10, full_node_2.mempool_manager.get_spendbundle, tx.spend_bundle, tx.name()) # Farm another block for i in range(1, 8): await full_node_1.farm_new_block( FarmNewBlockProtocol(token_bytes())) funds = sum([ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, num_blocks) ]) await time_out_assert( 10, wallet_0.wallet_state_manager.main_wallet.get_confirmed_balance, (funds - 10), ) await time_out_assert( 15, wallet_1.wallet_state_manager.main_wallet.get_confirmed_balance, (10))
async def test_mempool_tx_sync(self, three_nodes_two_wallets): num_blocks = 5 full_nodes, wallets = three_nodes_two_wallets wallet_0, wallet_server_0 = wallets[0] full_node_0, server_0 = full_nodes[0] full_node_1, server_1 = full_nodes[1] full_node_2, server_2 = full_nodes[2] ph = await wallet_0.wallet_state_manager.main_wallet.get_new_puzzlehash( ) # wallet0 <-> sever0 <-> server1 await wallet_server_0.start_client( PeerInfo("localhost", uint16(server_0._port)), None) await server_0.start_client( PeerInfo("localhost", uint16(server_1._port)), None) for i in range(1, num_blocks): await full_node_0.farm_new_block(FarmNewBlockProtocol(ph)) all_blocks = await full_node_0.get_current_blocks( full_node_0.get_tip()) for block in all_blocks: async for _ in full_node_2.respond_block( full_node_protocol.RespondBlock(block)): pass funds = sum([ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, num_blocks - 2) ]) await time_out_assert( 10, wallet_0.wallet_state_manager.main_wallet.get_confirmed_balance, funds) tx = (await wallet_0.wallet_state_manager.main_wallet. generate_signed_transaction(10, token_bytes(), 0)) await wallet_0.wallet_state_manager.main_wallet.push_transaction(tx) await time_out_assert(10, full_node_0.mempool_manager.get_spendbundle, tx.spend_bundle, tx.name()) await time_out_assert(10, full_node_1.mempool_manager.get_spendbundle, tx.spend_bundle, tx.name()) await time_out_assert(10, full_node_2.mempool_manager.get_spendbundle, None, tx.name()) # make a final connection. # wallet0 <-> sever0 <-> server1 <-> server2 await server_1.start_client( PeerInfo("localhost", uint16(server_2._port)), None) await time_out_assert(10, full_node_0.mempool_manager.get_spendbundle, tx.spend_bundle, tx.name()) await time_out_assert(10, full_node_1.mempool_manager.get_spendbundle, tx.spend_bundle, tx.name()) await time_out_assert(10, full_node_2.mempool_manager.get_spendbundle, tx.spend_bundle, tx.name())
async def respond_proof_of_space( self, response: harvester_protocol.RespondProofOfSpace): """ This is a response from the harvester with a proof of space. We check it's validity, and request a pool partial, a header signature, or both, if the proof is good enough. """ if response.proof.pool_pubkey not in self.pool_public_keys: raise RuntimeError("Pool pubkey not in list of approved keys") challenge_hash: bytes32 = self.harvester_responses_challenge[ response.quality_string] challenge_weight: uint128 = self.challenge_to_weight[challenge_hash] challenge_height: uint32 = self.challenge_to_height[challenge_hash] new_proof_height: uint32 = uint32(challenge_height + 1) difficulty: uint64 = uint64(0) for posf in self.challenges[challenge_weight]: if posf.challenge_hash == challenge_hash: difficulty = posf.difficulty if difficulty == 0: raise RuntimeError("Did not find challenge") computed_quality_string = response.proof.verify_and_get_quality_string( ) if response.quality_string != computed_quality_string: raise RuntimeError("Invalid quality for proof of space") self.harvester_responses_proofs[ response.quality_string] = response.proof self.harvester_responses_proof_hash_to_qual[ response.proof.get_hash()] = response.quality_string estimate_min = (self.proof_of_time_estimate_ips * self.constants["BLOCK_TIME_TARGET"] / self.constants["MIN_ITERS_PROPORTION"]) number_iters: uint64 = calculate_iterations_quality( computed_quality_string, response.proof.size, difficulty, estimate_min, ) estimate_secs: float = number_iters / self.proof_of_time_estimate_ips if estimate_secs < self.config["pool_share_threshold"]: request1 = harvester_protocol.RequestPartialProof( response.quality_string, self.wallet_target, ) yield OutboundMessage( NodeType.HARVESTER, Message("request_partial_proof", request1), Delivery.RESPOND, ) if estimate_secs < self.config["propagate_threshold"]: pool_pk = bytes(response.proof.pool_pubkey) if pool_pk not in self.pool_sks_map: log.error( f"Don't have the private key for the pool key used by harvester: {pool_pk.hex()}" ) return sk = self.pool_sks_map[pool_pk] coinbase_reward = uint64( calculate_block_reward(uint32(new_proof_height))) coinbase, signature = create_coinbase_coin_and_signature( new_proof_height, self.pool_target, coinbase_reward, sk, ) request2 = farmer_protocol.RequestHeaderHash( challenge_hash, coinbase, signature, self.wallet_target, response.proof, ) yield OutboundMessage( NodeType.FULL_NODE, Message("request_header_hash", request2), Delivery.BROADCAST, )
async def validate_unfinished_block_header( constants: Dict, headers: Dict[bytes32, Header], height_to_hash: Dict[uint32, bytes32], block_header: Header, proof_of_space: ProofOfSpace, prev_header_block: Optional[HeaderBlock], pre_validated: bool = False, pos_quality_string: bytes32 = None, ) -> Tuple[Optional[Err], Optional[uint64]]: """ Block validation algorithm. Returns the number of VDF iterations that this block's proof of time must have, if the candidate block is fully valid (except for proof of time). The same as validate_block, but without proof of time and challenge validation. If the block is invalid, an error code is returned. Does NOT validate transactions and fees. """ if not pre_validated: # 1. The hash of the proof of space must match header_data.proof_of_space_hash if proof_of_space.get_hash() != block_header.data.proof_of_space_hash: return (Err.INVALID_POSPACE_HASH, None) # 2. The coinbase signature must be valid, according the the pool public key pair = block_header.data.coinbase_signature.PkMessagePair( proof_of_space.pool_pubkey, block_header.data.coinbase.name(), ) if not block_header.data.coinbase_signature.validate([pair]): return (Err.INVALID_COINBASE_SIGNATURE, None) # 3. Check harvester signature of header data is valid based on harvester key if not block_header.harvester_signature.verify( [blspy.Util.hash256(block_header.data.get_hash())], [proof_of_space.plot_pubkey], ): return (Err.INVALID_HARVESTER_SIGNATURE, None) # 4. If not genesis, the previous block must exist if prev_header_block is not None and block_header.prev_header_hash not in headers: return (Err.DOES_NOT_EXTEND, None) # 5. If not genesis, the timestamp must be >= the average timestamp of last 11 blocks # and less than 2 hours in the future (if block height < 11, average all previous blocks). # Average is the sum, int diveded by the number of timestamps if prev_header_block is not None: last_timestamps: List[uint64] = [] curr = prev_header_block.header while len(last_timestamps) < constants["NUMBER_OF_TIMESTAMPS"]: last_timestamps.append(curr.data.timestamp) fetched = headers.get(curr.prev_header_hash, None) if not fetched: break curr = fetched if len(last_timestamps) != constants["NUMBER_OF_TIMESTAMPS"]: # For blocks 1 to 10, average timestamps of all previous blocks assert curr.height == 0 prev_time: uint64 = uint64( int(sum(last_timestamps) // len(last_timestamps))) if block_header.data.timestamp < prev_time: return (Err.TIMESTAMP_TOO_FAR_IN_PAST, None) if block_header.data.timestamp > time.time( ) + constants["MAX_FUTURE_TIME"]: return (Err.TIMESTAMP_TOO_FAR_IN_FUTURE, None) # 7. Extension data must be valid, if any is present # Compute challenge of parent challenge_hash: bytes32 if prev_header_block is not None: challenge: Challenge = prev_header_block.challenge challenge_hash = challenge.get_hash() # 8. Check challenge hash of prev is the same as in pos if challenge_hash != proof_of_space.challenge_hash: return (Err.INVALID_POSPACE_CHALLENGE, None) # 10. The proof of space must be valid on the challenge if pos_quality_string is None: pos_quality_string = proof_of_space.verify_and_get_quality_string() if not pos_quality_string: return (Err.INVALID_POSPACE, None) if prev_header_block is not None: # 11. If not genesis, the height on the previous block must be one less than on this block if block_header.height != prev_header_block.height + 1: return (Err.INVALID_HEIGHT, None) else: # 12. If genesis, the height must be 0 if block_header.height != 0: return (Err.INVALID_HEIGHT, None) # 13. The coinbase reward must match the block schedule coinbase_reward = calculate_block_reward(block_header.height) if coinbase_reward != block_header.data.coinbase.amount: return (Err.INVALID_COINBASE_AMOUNT, None) # 13b. The coinbase parent id must be the height if block_header.data.coinbase.parent_coin_info != block_header.height.to_bytes( 32, "big"): return (Err.INVALID_COINBASE_PARENT, None) # 13c. The fees coin parent id must be hash(hash(height)) if block_header.data.fees_coin.parent_coin_info != std_hash( std_hash(uint32(block_header.height))): return (Err.INVALID_FEES_COIN_PARENT, None) difficulty: uint64 if prev_header_block is not None: difficulty = get_next_difficulty(constants, headers, height_to_hash, prev_header_block.header) min_iters = get_next_min_iters(constants, headers, height_to_hash, prev_header_block) else: difficulty = uint64(constants["DIFFICULTY_STARTING"]) min_iters = uint64(constants["MIN_ITERS_STARTING"]) number_of_iters: uint64 = calculate_iterations_quality( pos_quality_string, proof_of_space.size, difficulty, min_iters, ) assert count_significant_bits(difficulty) <= constants["SIGNIFICANT_BITS"] assert count_significant_bits(min_iters) <= constants["SIGNIFICANT_BITS"] if prev_header_block is not None: # 17. If not genesis, the total weight must be the parent weight + difficulty if block_header.weight != prev_header_block.weight + difficulty: return (Err.INVALID_WEIGHT, None) # 18. If not genesis, the total iters must be parent iters + number_iters if (block_header.data.total_iters != prev_header_block.header.data.total_iters + number_of_iters): return (Err.INVALID_TOTAL_ITERS, None) else: # 19. If genesis, the total weight must be starting difficulty if block_header.weight != difficulty: return (Err.INVALID_WEIGHT, None) # 20. If genesis, the total iters must be number iters if block_header.data.total_iters != number_of_iters: return (Err.INVALID_TOTAL_ITERS, None) return (None, number_of_iters)
def _create_block( self, test_constants: ConsensusConstants, challenge_hash: bytes32, height: uint32, prev_header_hash: bytes32, prev_iters: uint64, prev_weight: uint128, timestamp: uint64, difficulty: int, min_iters: int, seed: bytes, genesis: bool = False, reward_puzzlehash: bytes32 = None, transactions: Program = None, aggsig: G2Element = None, fees: uint64 = uint64(0), ) -> FullBlock: """ Creates a block with the specified details. Uses the stored plots to create a proof of space, and also evaluates the VDF for the proof of time. """ selected_plot_info = None selected_proof_index = 0 selected_quality: Optional[bytes] = None best_quality = 0 plots = [ pinfo for _, pinfo in sorted(list(self.plots.items()), key=lambda x: str(x[0])) ] if self.use_any_pos: random.seed(seed) for i in range(len(plots) * 3): # Allow passing in seed, to create reorgs and different chains seeded_pn = random.randint(0, len(plots) - 1) plot_info = plots[seeded_pn] plot_id = plot_info.prover.get_id() ccp = ProofOfSpace.can_create_proof( plot_id, challenge_hash, test_constants.NUMBER_ZERO_BITS_CHALLENGE_SIG, ) if not ccp: continue qualities = plot_info.prover.get_qualities_for_challenge( challenge_hash) if len(qualities) > 0: selected_plot_info = plot_info selected_quality = qualities[0] break else: for i in range(len(plots)): plot_info = plots[i] j = 0 plot_id = plot_info.prover.get_id() ccp = ProofOfSpace.can_create_proof( plot_id, challenge_hash, test_constants.NUMBER_ZERO_BITS_CHALLENGE_SIG, ) if not ccp: continue qualities = plot_info.prover.get_qualities_for_challenge( challenge_hash) for quality in qualities: qual_int = int.from_bytes(quality, "big", signed=False) if qual_int > best_quality: best_quality = qual_int selected_quality = quality selected_plot_info = plot_info selected_proof_index = j j += 1 assert selected_plot_info is not None if selected_quality is None: raise RuntimeError("No proofs for this challenge") proof_xs: bytes = selected_plot_info.prover.get_full_proof( challenge_hash, selected_proof_index) plot_pk = ProofOfSpace.generate_plot_public_key( selected_plot_info.local_sk.get_g1(), selected_plot_info.farmer_public_key, ) proof_of_space: ProofOfSpace = ProofOfSpace( challenge_hash, selected_plot_info.pool_public_key, plot_pk, selected_plot_info.prover.get_size(), proof_xs, ) number_iters: uint64 = pot_iterations.calculate_iterations( proof_of_space, difficulty, min_iters, test_constants.NUMBER_ZERO_BITS_CHALLENGE_SIG, ) if self.real_plots: print(f"Performing {number_iters} VDF iterations") int_size = (test_constants.DISCRIMINANT_SIZE_BITS + 16) >> 4 result = prove(challenge_hash, test_constants.DISCRIMINANT_SIZE_BITS, number_iters) output = ClassgroupElement( int512(int.from_bytes( result[0:int_size], "big", signed=True, )), int512( int.from_bytes( result[int_size:2 * int_size], "big", signed=True, )), ) proof_bytes = result[2 * int_size:4 * int_size] proof_of_time = ProofOfTime( challenge_hash, number_iters, output, uint8(0), proof_bytes, ) # Use the extension data to create different blocks based on header hash extension_data: bytes32 = bytes32( [random.randint(0, 255) for _ in range(32)]) cost = uint64(0) fee_reward = uint64(block_rewards.calculate_base_fee(height) + fees) std_hash(std_hash(height)) # Create filter byte_array_tx: List[bytes32] = [] tx_additions: List[Coin] = [] tx_removals: List[bytes32] = [] if transactions: error, npc_list, _ = get_name_puzzle_conditions(transactions) additions: List[Coin] = additions_for_npc(npc_list) for coin in additions: tx_additions.append(coin) byte_array_tx.append(bytearray(coin.puzzle_hash)) for npc in npc_list: tx_removals.append(npc.coin_name) byte_array_tx.append(bytearray(npc.coin_name)) farmer_ph = self.farmer_ph pool_ph = self.pool_ph if reward_puzzlehash is not None: farmer_ph = reward_puzzlehash pool_ph = reward_puzzlehash byte_array_tx.append(bytearray(farmer_ph)) byte_array_tx.append(bytearray(pool_ph)) bip158: PyBIP158 = PyBIP158(byte_array_tx) encoded = bytes(bip158.GetEncoded()) removal_merkle_set = MerkleSet() addition_merkle_set = MerkleSet() # Create removal Merkle set for coin_name in tx_removals: removal_merkle_set.add_already_hashed(coin_name) # Create addition Merkle set puzzlehash_coin_map: Dict[bytes32, List[Coin]] = {} cb_reward = calculate_block_reward(height) cb_coin = create_coinbase_coin(height, pool_ph, cb_reward) fees_coin = create_fees_coin(height, farmer_ph, fee_reward) for coin in tx_additions + [cb_coin, fees_coin]: if coin.puzzle_hash in puzzlehash_coin_map: puzzlehash_coin_map[coin.puzzle_hash].append(coin) else: puzzlehash_coin_map[coin.puzzle_hash] = [coin] # Addition Merkle set contains puzzlehash and hash of all coins with that puzzlehash for puzzle, coins in puzzlehash_coin_map.items(): addition_merkle_set.add_already_hashed(puzzle) addition_merkle_set.add_already_hashed(hash_coin_list(coins)) additions_root = addition_merkle_set.get_root() removal_root = removal_merkle_set.get_root() generator_hash = (transactions.get_tree_hash() if transactions is not None else bytes32([0] * 32)) filter_hash = std_hash(encoded) pool_target = PoolTarget(pool_ph, uint32(height)) pool_target_signature = self.get_pool_key_signature( pool_target, proof_of_space.pool_public_key) assert pool_target_signature is not None final_aggsig: G2Element = pool_target_signature if aggsig is not None: final_aggsig = AugSchemeMPL.aggregate([final_aggsig, aggsig]) header_data: HeaderData = HeaderData( height, prev_header_hash, timestamp, filter_hash, proof_of_space.get_hash(), uint128(prev_weight + difficulty), uint64(prev_iters + number_iters), additions_root, removal_root, farmer_ph, fee_reward, pool_target, final_aggsig, cost, extension_data, generator_hash, ) header_hash_sig: G2Element = self.get_plot_signature( header_data, plot_pk) header: Header = Header(header_data, header_hash_sig) full_block: FullBlock = FullBlock(proof_of_space, proof_of_time, header, transactions, encoded) return full_block
async def test_wallet_make_transaction(self, two_wallet_nodes): test_rpc_port = uint16(21529) num_blocks = 5 full_nodes, wallets = two_wallet_nodes full_node_1, server_1 = full_nodes[0] wallet_node, server_2 = wallets[0] wallet_node_2, server_3 = wallets[1] wallet = wallet_node.wallet_state_manager.main_wallet wallet_2 = wallet_node_2.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() ph_2 = await wallet_2.get_new_puzzlehash() await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) for i in range(0, num_blocks): await full_node_1.farm_new_block(FarmNewBlockProtocol(ph)) initial_funds = sum([ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, num_blocks - 1) ]) initial_funds_eventually = sum([ calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i)) for i in range(1, num_blocks + 1) ]) wallet_rpc_api = WalletRpcApi(wallet_node) config = load_config(bt.root_path, "config.yaml") hostname = config["self_hostname"] daemon_port = config["daemon_port"] def stop_node_cb(): pass rpc_cleanup = await start_rpc_server( wallet_rpc_api, hostname, daemon_port, test_rpc_port, stop_node_cb, connect_to_daemon=False, ) await time_out_assert(5, wallet.get_confirmed_balance, initial_funds) await time_out_assert(5, wallet.get_unconfirmed_balance, initial_funds) client = await WalletRpcClient.create("localhost", test_rpc_port) try: addr = encode_puzzle_hash(await wallet_node_2.wallet_state_manager. main_wallet.get_new_puzzlehash()) tx_amount = 15600000 try: await client.send_transaction("1", 100000000000000000, addr) raise Exception("Should not create high value tx") except ValueError: pass tx = await client.send_transaction("1", tx_amount, addr) transaction_id = tx.name() async def tx_in_mempool(): tx = await client.get_transaction("1", transaction_id) return tx.is_in_mempool() await time_out_assert(5, tx_in_mempool, True) await time_out_assert(5, wallet.get_unconfirmed_balance, initial_funds - tx_amount) assert ( await client.get_wallet_balance("1") )["unconfirmed_wallet_balance"] == initial_funds - tx_amount assert (await client.get_wallet_balance("1") )["confirmed_wallet_balance"] == initial_funds for i in range(0, 5): await client.farm_block(encode_puzzle_hash(ph_2)) await asyncio.sleep(1) async def eventual_balance(): return ( await client.get_wallet_balance("1"))["confirmed_wallet_balance"] await time_out_assert(5, eventual_balance, initial_funds_eventually - tx_amount) address = await client.get_next_address("1") assert len(address) > 10 transactions = await client.get_transactions("1") assert len(transactions) > 1 pks = await client.get_public_keys() assert len(pks) == 1 assert (await client.get_height_info()) > 0 sk_dict = await client.get_private_key(pks[0]) assert sk_dict["fingerprint"] == pks[0] assert sk_dict["sk"] is not None assert sk_dict["pk"] is not None assert sk_dict["seed"] is not None mnemonic = await client.generate_mnemonic() assert len(mnemonic) == 24 await client.add_key(mnemonic) pks = await client.get_public_keys() assert len(pks) == 2 await client.log_in_and_skip(pks[1]) sk_dict = await client.get_private_key(pks[1]) assert sk_dict["fingerprint"] == pks[1] await client.delete_key(pks[0]) await client.log_in_and_skip(pks[1]) assert len(await client.get_public_keys()) == 1 assert not (await client.get_sync_status()) wallets = await client.get_wallets() assert len(wallets) == 1 balance = await client.get_wallet_balance(wallets[0]["id"]) assert balance["unconfirmed_wallet_balance"] == 0 test_wallet_backup_path = Path("test_wallet_backup_file") await client.create_backup(test_wallet_backup_path) assert test_wallet_backup_path.exists() test_wallet_backup_path.unlink() try: await client.send_transaction(wallets[0]["id"], 100, addr) raise Exception("Should not create tx if no balance") except ValueError: pass await client.delete_all_keys() assert len(await client.get_public_keys()) == 0 except Exception: # Checks that the RPC manages to stop the node client.close() await client.await_closed() await rpc_cleanup() raise client.close() await client.await_closed() await rpc_cleanup()
def _create_block( self, test_constants: Dict, challenge_hash: bytes32, height: uint32, prev_header_hash: bytes32, prev_iters: uint64, prev_weight: uint64, timestamp: uint64, difficulty: uint64, ips: uint64, seed: bytes, ) -> FullBlock: """ Creates a block with the specified details. Uses the stored plots to create a proof of space, and also evaluates the VDF for the proof of time. """ prover = None plot_pk = None plot_sk = None qualities: List[bytes] = [] for pn in range(num_plots): # Allow passing in seed, to create reorgs and different chains seeded_pn = (pn + 17 * int.from_bytes(seed, "big")) % num_plots filename = self.filenames[seeded_pn] plot_pk = plot_pks[seeded_pn] plot_sk = plot_sks[seeded_pn] prover = DiskProver(os.path.join(self.plot_dir, filename)) qualities = prover.get_qualities_for_challenge(challenge_hash) if len(qualities) > 0: break assert prover assert plot_pk assert plot_sk if len(qualities) == 0: raise NoProofsOfSpaceFound("No proofs for this challenge") proof_xs: bytes = prover.get_full_proof(challenge_hash, 0) proof_of_space: ProofOfSpace = ProofOfSpace( challenge_hash, pool_pk, plot_pk, k, [uint8(b) for b in proof_xs]) number_iters: uint64 = pot_iterations.calculate_iterations( proof_of_space, difficulty, ips, test_constants["MIN_BLOCK_TIME"]) disc: int = create_discriminant( challenge_hash, test_constants["DISCRIMINANT_SIZE_BITS"]) start_x: ClassGroup = ClassGroup.from_ab_discriminant(2, 1, disc) y_cl, proof_bytes = create_proof_of_time_nwesolowski( disc, start_x, number_iters, disc, n_wesolowski) output = ClassgroupElement(y_cl[0], y_cl[1]) proof_of_time = ProofOfTime( challenge_hash, number_iters, output, n_wesolowski, [uint8(b) for b in proof_bytes], ) coinbase: CoinbaseInfo = CoinbaseInfo( height, block_rewards.calculate_block_reward(uint32(height)), coinbase_target, ) coinbase_sig: PrependSignature = pool_sk.sign_prepend(bytes(coinbase)) fees_target: FeesTarget = FeesTarget(fee_target, uint64(0)) solutions_generator: bytes32 = sha256(seed).digest() cost = uint64(0) body: Body = Body(coinbase, coinbase_sig, fees_target, None, solutions_generator, cost) header_data: HeaderData = HeaderData( prev_header_hash, timestamp, bytes([0] * 32), proof_of_space.get_hash(), body.get_hash(), bytes([0] * 32), ) header_hash_sig: PrependSignature = plot_sk.sign_prepend( header_data.get_hash()) header: Header = Header(header_data, header_hash_sig) challenge = Challenge( challenge_hash, proof_of_space.get_hash(), proof_of_time.get_hash(), height, uint64(prev_weight + difficulty), uint64(prev_iters + number_iters), ) header_block = HeaderBlock(proof_of_space, proof_of_time, challenge, header) full_block: FullBlock = FullBlock(header_block, body) return full_block
def _create_block( self, test_constants: Dict, challenge_hash: bytes32, height: uint32, prev_header_hash: bytes32, prev_iters: uint64, prev_weight: uint128, timestamp: uint64, difficulty: uint64, min_iters: uint64, seed: bytes, genesis: bool = False, reward_puzzlehash: bytes32 = None, transactions: Program = None, aggsig: BLSSignature = None, fees: uint64 = uint64(0), ) -> FullBlock: """ Creates a block with the specified details. Uses the stored plots to create a proof of space, and also evaluates the VDF for the proof of time. """ selected_prover = None selected_plot_sk = None selected_pool_sk = None selected_proof_index = 0 plots = list(self.plot_config["plots"].items()) selected_quality: Optional[bytes] = None best_quality = 0 if self.use_any_pos: for i in range(len(plots) * 3): # Allow passing in seed, to create reorgs and different chains random.seed(seed + i.to_bytes(4, "big")) seeded_pn = random.randint(0, len(plots) - 1) pool_sk = PrivateKey.from_bytes( bytes.fromhex(plots[seeded_pn][1]["pool_sk"]) ) plot_sk = PrivateKey.from_bytes( bytes.fromhex(plots[seeded_pn][1]["sk"]) ) prover = DiskProver(plots[seeded_pn][0]) qualities = prover.get_qualities_for_challenge(challenge_hash) if len(qualities) > 0: if self.use_any_pos: selected_quality = qualities[0] selected_prover = prover selected_pool_sk = pool_sk selected_plot_sk = plot_sk break else: for i in range(len(plots)): pool_sk = PrivateKey.from_bytes(bytes.fromhex(plots[i][1]["pool_sk"])) plot_sk = PrivateKey.from_bytes(bytes.fromhex(plots[i][1]["sk"])) prover = DiskProver(plots[i][0]) qualities = prover.get_qualities_for_challenge(challenge_hash) j = 0 for quality in qualities: qual_int = int.from_bytes(quality, "big", signed=False) if qual_int > best_quality: best_quality = qual_int selected_quality = quality selected_prover = prover selected_pool_sk = pool_sk selected_plot_sk = plot_sk selected_proof_index = j j += 1 assert selected_prover assert selected_pool_sk assert selected_plot_sk pool_pk = selected_pool_sk.get_public_key() plot_pk = selected_plot_sk.get_public_key() if selected_quality is None: raise RuntimeError("No proofs for this challenge") proof_xs: bytes = selected_prover.get_full_proof( challenge_hash, selected_proof_index ) proof_of_space: ProofOfSpace = ProofOfSpace( challenge_hash, pool_pk, plot_pk, selected_prover.get_size(), proof_xs ) number_iters: uint64 = pot_iterations.calculate_iterations( proof_of_space, difficulty, min_iters ) # print("Doing iters", number_iters) int_size = (test_constants["DISCRIMINANT_SIZE_BITS"] + 16) >> 4 result = prove( challenge_hash, test_constants["DISCRIMINANT_SIZE_BITS"], number_iters ) output = ClassgroupElement( int512(int.from_bytes(result[0:int_size], "big", signed=True,)), int512( int.from_bytes(result[int_size : 2 * int_size], "big", signed=True,) ), ) proof_bytes = result[2 * int_size : 4 * int_size] proof_of_time = ProofOfTime( challenge_hash, number_iters, output, self.n_wesolowski, proof_bytes, ) if not reward_puzzlehash: reward_puzzlehash = self.fee_target # Use the extension data to create different blocks based on header hash extension_data: bytes32 = bytes32([random.randint(0, 255) for _ in range(32)]) cost = uint64(0) coinbase_reward = block_rewards.calculate_block_reward(height) fee_reward = uint64(block_rewards.calculate_base_fee(height) + fees) coinbase_coin, coinbase_signature = create_coinbase_coin_and_signature( height, reward_puzzlehash, coinbase_reward, selected_pool_sk ) parent_coin_name = std_hash(std_hash(height)) fees_coin = Coin(parent_coin_name, reward_puzzlehash, uint64(fee_reward)) # Create filter byte_array_tx: List[bytes32] = [] tx_additions: List[Coin] = [] tx_removals: List[bytes32] = [] encoded = None if transactions: error, npc_list, _ = get_name_puzzle_conditions(transactions) additions: List[Coin] = additions_for_npc(npc_list) for coin in additions: tx_additions.append(coin) byte_array_tx.append(bytearray(coin.puzzle_hash)) for npc in npc_list: tx_removals.append(npc.coin_name) byte_array_tx.append(bytearray(npc.coin_name)) bip158: PyBIP158 = PyBIP158(byte_array_tx) encoded = bytes(bip158.GetEncoded()) removal_merkle_set = MerkleSet() addition_merkle_set = MerkleSet() # Create removal Merkle set for coin_name in tx_removals: removal_merkle_set.add_already_hashed(coin_name) # Create addition Merkle set puzzlehash_coin_map: Dict[bytes32, List[Coin]] = {} for coin in tx_additions: if coin.puzzle_hash in puzzlehash_coin_map: puzzlehash_coin_map[coin.puzzle_hash].append(coin) else: puzzlehash_coin_map[coin.puzzle_hash] = [coin] # Addition Merkle set contains puzzlehash and hash of all coins with that puzzlehash for puzzle, coins in puzzlehash_coin_map.items(): addition_merkle_set.add_already_hashed(puzzle) addition_merkle_set.add_already_hashed(hash_coin_list(coins)) additions_root = addition_merkle_set.get_root() removal_root = removal_merkle_set.get_root() generator_hash = ( transactions.get_tree_hash() if transactions is not None else bytes32([0] * 32) ) filter_hash = std_hash(encoded) if encoded is not None else bytes32([0] * 32) header_data: HeaderData = HeaderData( height, prev_header_hash, timestamp, filter_hash, proof_of_space.get_hash(), uint128(prev_weight + difficulty), uint64(prev_iters + number_iters), additions_root, removal_root, coinbase_coin, coinbase_signature, fees_coin, aggsig, cost, extension_data, generator_hash, ) header_hash_sig: PrependSignature = selected_plot_sk.sign_prepend( header_data.get_hash() ) header: Header = Header(header_data, header_hash_sig) full_block: FullBlock = FullBlock( proof_of_space, proof_of_time, header, transactions, encoded ) return full_block