async def test_assert_time_exceeds(self, two_nodes): num_blocks = 10 wallet_a = WALLET_A coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] receiver_puzzlehash = BURN_PUZZLE_HASH # Farm blocks blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash) full_node_1, full_node_2, server_1, server_2 = two_nodes for block in blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block)): pass # Coinbase that gets spent block1 = blocks[1] # This condition requires block1 coinbase to be spent after 3 seconds from now current_time_plus3 = uint64(int(time.time() * 1000) + 3000) block1_cvp = ConditionVarPair(ConditionOpcode.ASSERT_TIME_EXCEEDS, int_to_bytes(current_time_plus3), None) block1_dic = {block1_cvp.opcode: [block1_cvp]} block1_spend_bundle = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, block1.get_coinbase(), block1_dic) # program that will be sent to early assert block1_spend_bundle is not None program = best_solution_program(block1_spend_bundle) aggsig = block1_spend_bundle.aggregated_signature # Create another block that includes our transaction dic_h = {11: (program, aggsig)} invalid_new_blocks = bt.get_consecutive_blocks(test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h) # Try to validate that block before 3 sec next_block = invalid_new_blocks[11] error = await full_node_1.blockchain._validate_transactions( next_block, next_block.get_fees_coin().amount) assert error is Err.ASSERT_TIME_EXCEEDS_FAILED # wait 3 sec to pass await asyncio.sleep(3.1) dic_h = {12: (program, aggsig)} valid_new_blocks = bt.get_consecutive_blocks(test_constants, 2, blocks[:11], 10, b"", coinbase_puzzlehash, dic_h) for block in valid_new_blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block)): pass # Try to validate that block after 3 sec have passed next_block = valid_new_blocks[12] error = await full_node_1.blockchain._validate_transactions( next_block, next_block.get_fees_coin().amount) assert error is None
async def test1(self, two_nodes): num_blocks = 5 test_rpc_port = uint16(21522) nodes, _ = two_nodes full_node_api_1, full_node_api_2 = nodes server_1 = full_node_api_1.full_node.server server_2 = full_node_api_2.full_node.server def stop_node_cb(): full_node_api_1._close() server_1.close_all() full_node_rpc_api = FullNodeRpcApi(full_node_api_1.full_node) config = bt.config hostname = config["self_hostname"] daemon_port = config["daemon_port"] rpc_cleanup = await start_rpc_server( full_node_rpc_api, hostname, daemon_port, test_rpc_port, stop_node_cb, bt.root_path, config, connect_to_daemon=False, ) try: client = await FullNodeRpcClient.create(self_hostname, test_rpc_port, bt.root_path, config) await validate_get_routes(client, full_node_rpc_api) state = await client.get_blockchain_state() assert state["peak"] is None assert not state["sync"]["sync_mode"] assert state["difficulty"] > 0 assert state["sub_slot_iters"] > 0 blocks = bt.get_consecutive_blocks(num_blocks) blocks = bt.get_consecutive_blocks( num_blocks, block_list_input=blocks, guarantee_transaction_block=True) assert len(await client.get_unfinished_block_headers()) == 0 assert len((await client.get_block_records(0, 100))) == 0 for block in blocks: if is_overflow_block( test_constants, block.reward_chain_block.signage_point_index): finished_ss = block.finished_sub_slots[:-1] else: finished_ss = block.finished_sub_slots unf = UnfinishedBlock( finished_ss, block.reward_chain_block.get_unfinished(), block.challenge_chain_sp_proof, block.reward_chain_sp_proof, block.foliage, block.foliage_transaction_block, block.transactions_info, block.transactions_generator, [], ) await full_node_api_1.full_node.respond_unfinished_block( full_node_protocol.RespondUnfinishedBlock(unf), None) await full_node_api_1.full_node.respond_block( full_node_protocol.RespondBlock(block), None) assert len(await client.get_unfinished_block_headers()) > 0 assert len(await client.get_all_block(0, 2)) == 2 state = await client.get_blockchain_state() block = await client.get_block(state["peak"].header_hash) assert block == blocks[-1] assert (await client.get_block(bytes([1] * 32))) is None assert (await client.get_block_record_by_height(2) ).header_hash == blocks[2].header_hash assert len((await client.get_block_records(0, 100))) == num_blocks * 2 assert (await client.get_block_record_by_height(100)) is None ph = list(blocks[-1].get_included_reward_coins())[0].puzzle_hash coins = await client.get_coin_records_by_puzzle_hash(ph) print(coins) assert len(coins) >= 1 pid = list( blocks[-1].get_included_reward_coins())[0].parent_coin_info pid_2 = list( blocks[-1].get_included_reward_coins())[1].parent_coin_info coins = await client.get_coin_records_by_parent_ids([pid, pid_2]) print(coins) assert len(coins) == 2 name = list(blocks[-1].get_included_reward_coins())[0].name() name_2 = list(blocks[-1].get_included_reward_coins())[1].name() coins = await client.get_coin_records_by_names([name, name_2]) print(coins) assert len(coins) == 2 additions, removals = await client.get_additions_and_removals( blocks[-1].header_hash) assert len(additions) >= 2 and len(removals) == 0 wallet = WalletTool(full_node_api_1.full_node.constants) wallet_receiver = WalletTool( full_node_api_1.full_node.constants, AugSchemeMPL.key_gen(std_hash(b"123123"))) ph = wallet.get_new_puzzlehash() ph_2 = wallet.get_new_puzzlehash() ph_receiver = wallet_receiver.get_new_puzzlehash() assert len(await client.get_coin_records_by_puzzle_hash(ph)) == 0 assert len( await client.get_coin_records_by_puzzle_hash(ph_receiver)) == 0 blocks = bt.get_consecutive_blocks( 2, block_list_input=blocks, guarantee_transaction_block=True, farmer_reward_puzzle_hash=ph, pool_reward_puzzle_hash=ph, ) for block in blocks[-2:]: await full_node_api_1.full_node.respond_block( full_node_protocol.RespondBlock(block)) assert len(await client.get_coin_records_by_puzzle_hash(ph)) == 2 assert len( await client.get_coin_records_by_puzzle_hash(ph_receiver)) == 0 coin_to_spend = list(blocks[-1].get_included_reward_coins())[0] spend_bundle = wallet.generate_signed_transaction( coin_to_spend.amount, ph_receiver, coin_to_spend) assert len(await client.get_all_mempool_items()) == 0 assert len(await client.get_all_mempool_tx_ids()) == 0 assert (await client.get_mempool_item_by_tx_id(spend_bundle.name() )) is None await client.push_tx(spend_bundle) coin = spend_bundle.additions()[0] assert len(await client.get_all_mempool_items()) == 1 assert len(await client.get_all_mempool_tx_ids()) == 1 assert (SpendBundle.from_json_dict( list((await client.get_all_mempool_items() ).values())[0]["spend_bundle"]) == spend_bundle) assert (await client.get_all_mempool_tx_ids())[0] == spend_bundle.name() assert (SpendBundle.from_json_dict( (await client.get_mempool_item_by_tx_id( spend_bundle.name()))["spend_bundle"]) == spend_bundle) assert (await client.get_coin_record_by_name(coin.name())) is None await full_node_api_1.farm_new_transaction_block( FarmNewBlockProtocol(ph_2)) assert (await client.get_coin_record_by_name(coin.name())).coin == coin assert len( await client.get_coin_records_by_puzzle_hash(ph_receiver)) == 1 assert len( list( filter(lambda cr: not cr.spent, (await client.get_coin_records_by_puzzle_hash(ph))))) == 3 assert len(await client.get_coin_records_by_puzzle_hashes( [ph_receiver, ph])) == 5 assert len(await client.get_coin_records_by_puzzle_hash(ph, False)) == 3 assert len(await client.get_coin_records_by_puzzle_hash(ph, True)) == 4 assert len(await client.get_coin_records_by_puzzle_hash( ph, True, 0, 100)) == 4 assert len(await client.get_coin_records_by_puzzle_hash( ph, True, 50, 100)) == 0 assert len(await client.get_coin_records_by_puzzle_hash( ph, True, 0, blocks[-1].height + 1)) == 2 assert len(await client.get_coin_records_by_puzzle_hash(ph, True, 0, 1)) == 0 assert len(await client.get_connections()) == 0 await client.open_connection(self_hostname, server_2._port) async def num_connections(): return len(await client.get_connections()) await time_out_assert(10, num_connections, 1) connections = await client.get_connections() assert NodeType(connections[0]["type"]) == NodeType.FULL_NODE.value assert len(await client.get_connections(NodeType.FULL_NODE)) == 1 assert len(await client.get_connections(NodeType.FARMER)) == 0 await client.close_connection(connections[0]["node_id"]) await time_out_assert(10, num_connections, 0) blocks: List[FullBlock] = await client.get_blocks(0, 5) assert len(blocks) == 5 await full_node_api_1.reorg_from_index_to_new_index( ReorgProtocol(2, 55, bytes([0x2] * 32))) new_blocks_0: List[FullBlock] = await client.get_blocks(0, 5) assert len(new_blocks_0) == 7 new_blocks: List[FullBlock] = await client.get_blocks( 0, 5, exclude_reorged=True) assert len(new_blocks) == 5 assert blocks[0].header_hash == new_blocks[0].header_hash assert blocks[1].header_hash == new_blocks[1].header_hash assert blocks[2].header_hash == new_blocks[2].header_hash assert blocks[3].header_hash != new_blocks[3].header_hash finally: # Checks that the RPC manages to stop the node client.close() await client.await_closed() await rpc_cleanup()
async def test_basic_coin_store(self): wallet_a = WALLET_A reward_ph = wallet_a.get_new_puzzlehash() # Generate some coins blocks = bt.get_consecutive_blocks( 10, [], farmer_reward_puzzle_hash=reward_ph, pool_reward_puzzle_hash=reward_ph, ) coins_to_spend: List[Coin] = [] for block in blocks: if block.is_block(): for coin in block.get_included_reward_coins(): if coin.puzzle_hash == reward_ph: coins_to_spend.append(coin) spend_bundle = wallet_a.generate_signed_transaction(1000, wallet_a.get_new_puzzlehash(), coins_to_spend[0]) db_path = Path("fndb_test.db") if db_path.exists(): db_path.unlink() connection = await aiosqlite.connect(db_path) coin_store = await CoinStore.create(connection) blocks = bt.get_consecutive_blocks( 10, blocks, farmer_reward_puzzle_hash=reward_ph, pool_reward_puzzle_hash=reward_ph, transaction_data=spend_bundle, ) # Adding blocks to the coin store should_be_included_prev: Set[Coin] = set() should_be_included: Set[Coin] = set() for block in blocks: farmer_coin, pool_coin = get_future_reward_coins(block) should_be_included.add(farmer_coin) should_be_included.add(pool_coin) if block.is_block(): removals, additions = block.tx_removals_and_additions() assert block.get_included_reward_coins() == should_be_included_prev await coin_store.new_block(block) for expected_coin in should_be_included_prev: # Check that the coinbase rewards are added record = await coin_store.get_coin_record(expected_coin.name()) assert record is not None assert not record.spent assert record.coin == expected_coin for coin_name in removals: # Check that the removed coins are set to spent record = await coin_store.get_coin_record(coin_name) assert record.spent for coin in additions: # Check that the added coins are added record = await coin_store.get_coin_record(coin.name()) assert not record.spent assert coin == record.coin should_be_included_prev = should_be_included.copy() should_be_included = set() await connection.close() Path("fndb_test.db").unlink()
async def test_assert_my_coin_id(self, two_nodes): num_blocks = 10 wallet_a = WALLET_A coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] receiver_puzzlehash = BURN_PUZZLE_HASH # Farm blocks blocks = bt.get_consecutive_blocks( num_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True ) full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes full_node_1 = full_node_api_1.full_node for block in blocks: await full_node_api_1.full_node.respond_block(full_node_protocol.RespondBlock(block)) # Coinbase that gets spent spend_block = blocks[2] bad_block = blocks[3] spend_coin = None bad_spend_coin = None for coin in list(spend_block.get_included_reward_coins()): if coin.puzzle_hash == coinbase_puzzlehash: spend_coin = coin for coin in list(bad_block.get_included_reward_coins()): if coin.puzzle_hash == coinbase_puzzlehash: bad_spend_coin = coin valid_cvp = ConditionVarPair(ConditionOpcode.ASSERT_MY_COIN_ID, [spend_coin.name()]) valid_dic = {valid_cvp.opcode: [valid_cvp]} bad_cvp = ConditionVarPair(ConditionOpcode.ASSERT_MY_COIN_ID, [bad_spend_coin.name()]) bad_dic = {bad_cvp.opcode: [bad_cvp]} bad_spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_puzzlehash, spend_coin, bad_dic) valid_spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_puzzlehash, spend_coin, valid_dic) assert bad_spend_bundle is not None assert valid_spend_bundle is not None # Invalid block bundle # Create another block that includes our transaction invalid_new_blocks = bt.get_consecutive_blocks( 1, blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, transaction_data=bad_spend_bundle, guarantee_transaction_block=True, ) # Try to validate that block res, err, _ = await full_node_1.blockchain.receive_block(invalid_new_blocks[-1]) assert res == ReceiveBlockResult.INVALID_BLOCK assert err == Err.ASSERT_MY_COIN_ID_FAILED # Valid block bundle # Create another block that includes our transaction new_blocks = bt.get_consecutive_blocks( 1, blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, transaction_data=valid_spend_bundle, guarantee_transaction_block=True, ) res, err, _ = await full_node_1.blockchain.receive_block(new_blocks[-1]) assert res == ReceiveBlockResult.NEW_PEAK assert err is None
async def test_assert_block_age_exceeds(self, two_nodes): num_blocks = 11 wallet_a = WALLET_A coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] receiver_puzzlehash = BURN_PUZZLE_HASH # Farm blocks blocks = bt.get_consecutive_blocks( num_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True ) full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes full_node_1 = full_node_api_1.full_node for block in blocks: await full_node_api_1.full_node.respond_block(full_node_protocol.RespondBlock(block)) # Coinbase that gets spent block1 = blocks[2] spend_coin_block_1 = None for coin in list(block1.get_included_reward_coins()): if coin.puzzle_hash == coinbase_puzzlehash: spend_coin_block_1 = coin # This condition requires block1 coinbase to be spent after index 11 # This condition requires block1 coinbase to be spent more than 10 block after it was farmed # block index has to be greater than (2 + 9 = 11) block1_cvp = ConditionVarPair(ConditionOpcode.ASSERT_HEIGHT_AGE_EXCEEDS, [int_to_bytes(9)]) block1_dic = {block1_cvp.opcode: [block1_cvp]} block1_spend_bundle = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, spend_coin_block_1, block1_dic ) # program that will be sent too early assert block1_spend_bundle is not None invalid_new_blocks = bt.get_consecutive_blocks( 1, blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, transaction_data=block1_spend_bundle, guarantee_transaction_block=True, ) # Try to validate that block at index 11 res, err, _ = await full_node_1.blockchain.receive_block(invalid_new_blocks[-1]) assert res == ReceiveBlockResult.INVALID_BLOCK assert err == Err.ASSERT_HEIGHT_AGE_EXCEEDS_FAILED new_blocks = bt.get_consecutive_blocks( 1, blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True, ) res, _, _ = await full_node_1.blockchain.receive_block(new_blocks[-1]) assert res == ReceiveBlockResult.NEW_PEAK # At index 12, it can be spent new_blocks = bt.get_consecutive_blocks( 1, new_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, transaction_data=block1_spend_bundle, guarantee_transaction_block=True, ) res, err, _ = await full_node_1.blockchain.receive_block(new_blocks[-1]) assert err is None assert res == ReceiveBlockResult.NEW_PEAK
async def test_basic_reorg(self): initial_block_count = 30 reorg_length = 15 blocks = bt.get_consecutive_blocks(initial_block_count) db_path = Path("blockchain_test.db") if db_path.exists(): db_path.unlink() connection = await aiosqlite.connect(db_path) coin_store = await CoinStore.create(connection) store = await BlockStore.create(connection) b: Blockchain = await Blockchain.create(coin_store, store, test_constants) try: for block in blocks: await b.receive_block(block) assert b.get_peak().height == initial_block_count - 1 for c, block in enumerate(blocks): if block.is_transaction_block(): coins = block.get_included_reward_coins() records: List[Optional[CoinRecord]] = [ await coin_store.get_coin_record(coin.name()) for coin in coins ] for record in records: assert not record.spent assert record.confirmed_block_index == block.height assert record.spent_block_index == 0 blocks_reorg_chain = bt.get_consecutive_blocks( reorg_length, blocks[:initial_block_count - 10], seed=b"2") for reorg_block in blocks_reorg_chain: result, error_code, _ = await b.receive_block(reorg_block) print( f"Height {reorg_block.height} {initial_block_count - 10} result {result}" ) if reorg_block.height < initial_block_count - 10: assert result == ReceiveBlockResult.ALREADY_HAVE_BLOCK elif reorg_block.height < initial_block_count - 1: assert result == ReceiveBlockResult.ADDED_AS_ORPHAN elif reorg_block.height >= initial_block_count: assert result == ReceiveBlockResult.NEW_PEAK if reorg_block.is_transaction_block(): coins = reorg_block.get_included_reward_coins() records: List[Optional[CoinRecord]] = [ await coin_store.get_coin_record(coin.name()) for coin in coins ] for record in records: assert not record.spent assert record.confirmed_block_index == reorg_block.height assert record.spent_block_index == 0 assert error_code is None assert b.get_peak( ).height == initial_block_count - 10 + reorg_length - 1 except Exception as e: await connection.close() Path("blockchain_test.db").unlink() b.shut_down() raise e await connection.close() Path("blockchain_test.db").unlink() b.shut_down()
async def test_validate_blockchain_with_reorg_double_spend(self, two_nodes): num_blocks = 10 wallet_a = WALLET_A coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] receiver_puzzlehash = BURN_PUZZLE_HASH blocks = bt.get_consecutive_blocks( num_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True ) full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes for block in blocks: await full_node_api_1.full_node.respond_block(full_node_protocol.RespondBlock(block)) spend_block = blocks[2] spend_coin = None for coin in list(spend_block.get_included_reward_coins()): if coin.puzzle_hash == coinbase_puzzlehash: spend_coin = coin spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_puzzlehash, spend_coin) blocks_spend = bt.get_consecutive_blocks( 1, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True, transaction_data=spend_bundle, ) # Move chain to height 10, with a spend at height 10 for block in blocks_spend: await full_node_api_1.full_node.respond_block(full_node_protocol.RespondBlock(block)) # Reorg at height 5, add up to and including height 12 new_blocks = bt.get_consecutive_blocks( 7, blocks[:6], farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True, seed=b"another seed", ) for block in new_blocks: await full_node_api_1.full_node.respond_block(full_node_protocol.RespondBlock(block)) # Spend the same coin in the new reorg chain at height 13 new_blocks = bt.get_consecutive_blocks( 1, new_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True, transaction_data=spend_bundle, ) res, err, _ = await full_node_api_1.full_node.blockchain.receive_block(new_blocks[-1]) assert err is None assert res == ReceiveBlockResult.NEW_PEAK # But can't spend it twice new_blocks_double = bt.get_consecutive_blocks( 1, new_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True, transaction_data=spend_bundle, ) res, err, _ = await full_node_api_1.full_node.blockchain.receive_block(new_blocks_double[-1]) assert err is Err.DOUBLE_SPEND assert res == ReceiveBlockResult.INVALID_BLOCK # Now test Reorg at block 5, same spend at block height 12 new_blocks_reorg = bt.get_consecutive_blocks( 1, new_blocks[:12], farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True, transaction_data=spend_bundle, seed=b"spend at 12 is ok", ) for block in new_blocks_reorg: await full_node_api_1.full_node.respond_block(full_node_protocol.RespondBlock(block)) # Spend at height 13 is also OK (same height) new_blocks_reorg = bt.get_consecutive_blocks( 1, new_blocks[:13], farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True, transaction_data=spend_bundle, seed=b"spend at 13 is ok", ) for block in new_blocks_reorg: await full_node_api_1.full_node.respond_block(full_node_protocol.RespondBlock(block)) # Spend at height 14 is not OK (already spend) new_blocks_reorg = bt.get_consecutive_blocks( 1, new_blocks[:14], farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True, transaction_data=spend_bundle, seed=b"spend at 14 is double spend", ) with pytest.raises(ConsensusError): for block in new_blocks_reorg: await full_node_api_1.full_node.respond_block(full_node_protocol.RespondBlock(block))
async def test_weight_proof_edge_cases(self, default_400_blocks): blocks: List[FullBlock] = default_400_blocks blocks: List[FullBlock] = bt.get_consecutive_blocks( 1, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, skip_slots=2) blocks: List[FullBlock] = bt.get_consecutive_blocks( 1, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, skip_slots=1) blocks: List[FullBlock] = bt.get_consecutive_blocks( 1, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, ) blocks: List[FullBlock] = bt.get_consecutive_blocks( 1, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, skip_slots=2) blocks: List[FullBlock] = bt.get_consecutive_blocks( 1, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, ) blocks: List[FullBlock] = bt.get_consecutive_blocks( 1, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, ) blocks: List[FullBlock] = bt.get_consecutive_blocks( 1, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, ) blocks: List[FullBlock] = bt.get_consecutive_blocks( 1, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, ) blocks: List[FullBlock] = bt.get_consecutive_blocks( 1, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, skip_slots=4, normalized_to_identity_cc_eos=True, ) blocks: List[FullBlock] = bt.get_consecutive_blocks( 10, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, ) blocks: List[FullBlock] = bt.get_consecutive_blocks( 1, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, skip_slots=4, normalized_to_identity_icc_eos=True, ) blocks: List[FullBlock] = bt.get_consecutive_blocks( 10, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, ) blocks: List[FullBlock] = bt.get_consecutive_blocks( 1, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, skip_slots=4, normalized_to_identity_cc_ip=True, ) blocks: List[FullBlock] = bt.get_consecutive_blocks( 10, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, ) blocks: List[FullBlock] = bt.get_consecutive_blocks( 1, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, skip_slots=4, normalized_to_identity_cc_sp=True, ) blocks: List[FullBlock] = bt.get_consecutive_blocks( 1, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, skip_slots=4) blocks: List[FullBlock] = bt.get_consecutive_blocks( 10, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=True, ) blocks: List[FullBlock] = bt.get_consecutive_blocks( 300, block_list_input=blocks, seed=b"asdfghjkl", force_overflow=False, ) header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate( blocks) wpf = WeightProofHandler( test_constants, BlockCache(sub_blocks, header_cache, height_to_hash, summaries)) wp = await wpf.get_proof_of_weight(blocks[-1].header_hash) assert wp is not None wpf = WeightProofHandler( test_constants, BlockCache(sub_blocks, header_cache, height_to_hash, {})) valid, fork_point = wpf.validate_weight_proof_single_proc(wp) assert valid assert fork_point == 0
async def test_block_store(self): assert sqlite3.threadsafety == 1 blocks = bt.get_consecutive_blocks(test_constants, 9, [], 9, b"0") blocks_alt = bt.get_consecutive_blocks(test_constants, 3, [], 9, b"1") db_filename = Path("blockchain_test.db") db_filename_2 = Path("blockchain_test_2.db") db_filename_3 = Path("blockchain_test_3.db") if db_filename.exists(): db_filename.unlink() if db_filename_2.exists(): db_filename_2.unlink() if db_filename_3.exists(): db_filename_3.unlink() connection = await aiosqlite.connect(db_filename) connection_2 = await aiosqlite.connect(db_filename_2) connection_3 = await aiosqlite.connect(db_filename_3) db = await BlockStore.create(connection) # db_2 = await BlockStore.create(connection_2) await BlockStore.create(connection_2) db_3 = await BlockStore.create(connection_3) try: genesis = FullBlock.from_bytes(test_constants["GENESIS_BLOCK"]) # Save/get block for block in blocks: await db.add_block(block) assert block == await db.get_block(block.header_hash) await db.add_block(blocks_alt[2]) assert len(await db.get_blocks_at([1, 2])) == 3 # Get headers (added alt block also, so +1) assert len(await db.get_headers()) == len(blocks) + 1 # Test LCA assert (await db.get_lca()) is None await db.set_lca(blocks[-3].header_hash) assert (await db.get_lca()) == blocks[-3].header await db.set_tips([blocks[-2].header_hash, blocks[-1].header_hash]) assert (await db.get_tips()) == [blocks[-2].header, blocks[-1].header] coin_store: CoinStore = await CoinStore.create(connection_3) b: Blockchain = await Blockchain.create(coin_store, db_3, test_constants) assert b.lca_block == genesis.header assert b.tips == [genesis.header] assert await db_3.get_lca() == genesis.header assert await db_3.get_tips() == [genesis.header] for block in blocks: await b.receive_block(block) assert b.lca_block == blocks[-3].header assert set(b.tips) == set( [blocks[-3].header, blocks[-2].header, blocks[-1].header] ) left = sorted(b.tips, key=lambda t: t.height) right = sorted((await db_3.get_tips()), key=lambda t: t.height) assert left == right except Exception: await connection.close() await connection_2.close() await connection_3.close() db_filename.unlink() db_filename_2.unlink() db_filename_3.unlink() b.shut_down() raise await connection.close() await connection_2.close() await connection_3.close() db_filename.unlink() db_filename_2.unlink() db_filename_3.unlink() b.shut_down()
async def test1(self, two_nodes): num_blocks = 5 test_rpc_port = uint16(21522) full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes def stop_node_cb(): full_node_api_1._close() server_1.close_all() full_node_rpc_api = FullNodeRpcApi(full_node_api_1.full_node) config = bt.config hostname = config["self_hostname"] daemon_port = config["daemon_port"] rpc_cleanup = await start_rpc_server( full_node_rpc_api, hostname, daemon_port, test_rpc_port, stop_node_cb, bt.root_path, config, connect_to_daemon=False, ) try: client = await FullNodeRpcClient.create(self_hostname, test_rpc_port, bt.root_path, config) state = await client.get_blockchain_state() assert state["peak"] is None assert not state["sync"]["sync_mode"] assert state["difficulty"] > 0 assert state["sub_slot_iters"] > 0 blocks = bt.get_consecutive_blocks(num_blocks) blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, guarantee_block=True) assert len(await client.get_unfinished_sub_block_headers()) == 0 assert len((await client.get_sub_block_records(0, 100))) == 0 for block in blocks: if is_overflow_sub_block( test_constants, block.reward_chain_sub_block.signage_point_index): finished_ss = block.finished_sub_slots[:-1] else: finished_ss = block.finished_sub_slots unf = UnfinishedBlock( finished_ss, block.reward_chain_sub_block.get_unfinished(), block.challenge_chain_sp_proof, block.reward_chain_sp_proof, block.foliage_sub_block, block.foliage_block, block.transactions_info, block.transactions_generator, ) await full_node_api_1.full_node.respond_unfinished_sub_block( full_node_protocol.RespondUnfinishedSubBlock(unf), None) await full_node_api_1.full_node.respond_sub_block( full_node_protocol.RespondSubBlock(block), None) assert len(await client.get_unfinished_sub_block_headers()) > 0 assert len(await client.get_all_block(0, 2)) == 2 state = await client.get_blockchain_state() block = await client.get_sub_block(state["peak"].header_hash) assert block == blocks[-1] assert (await client.get_sub_block(bytes([1] * 32))) is None assert (await client.get_sub_block_record_by_height(2) ).header_hash == blocks[2].header_hash assert len( (await client.get_sub_block_records(0, 100))) == num_blocks + 1 assert (await client.get_sub_block_record_by_height(100)) is None ph = list(blocks[-1].get_included_reward_coins())[0].puzzle_hash coins = await client.get_unspent_coins(ph) assert len(coins) >= 1 additions, removals = await client.get_additions_and_removals( blocks[-1].header_hash) assert len(additions) >= 2 and len(removals) == 0 assert len(await client.get_connections()) == 0 await client.open_connection(self_hostname, server_2._port) async def num_connections(): return len(await client.get_connections()) await time_out_assert(10, num_connections, 1) connections = await client.get_connections() await client.close_connection(connections[0]["node_id"]) await time_out_assert(10, num_connections, 0) finally: # Checks that the RPC manages to stop the node client.close() await client.await_closed() await rpc_cleanup()
async def test_basic_coin_store(self): wallet_a = WALLET_A reward_ph = wallet_a.get_new_puzzlehash() for cache_size in [0]: # Generate some coins blocks = bt.get_consecutive_blocks( 10, [], farmer_reward_puzzle_hash=reward_ph, pool_reward_puzzle_hash=reward_ph, ) coins_to_spend: List[Coin] = [] for block in blocks: if block.is_transaction_block(): for coin in block.get_included_reward_coins(): if coin.puzzle_hash == reward_ph: coins_to_spend.append(coin) spend_bundle = wallet_a.generate_signed_transaction( 1000, wallet_a.get_new_puzzlehash(), coins_to_spend[0]) db_path = Path("fndb_test.db") if db_path.exists(): db_path.unlink() connection = await aiosqlite.connect(db_path) db_wrapper = DBWrapper(connection) coin_store = await CoinStore.create(db_wrapper, cache_size=uint32(cache_size)) blocks = bt.get_consecutive_blocks( 10, blocks, farmer_reward_puzzle_hash=reward_ph, pool_reward_puzzle_hash=reward_ph, transaction_data=spend_bundle, ) # Adding blocks to the coin store should_be_included_prev: Set[Coin] = set() should_be_included: Set[Coin] = set() for block in blocks: farmer_coin, pool_coin = get_future_reward_coins(block) should_be_included.add(farmer_coin) should_be_included.add(pool_coin) if block.is_transaction_block(): if block.transactions_generator is not None: block_gen: BlockGenerator = BlockGenerator( block.transactions_generator, []) npc_result = get_name_puzzle_conditions( block_gen, bt.constants.MAX_BLOCK_COST_CLVM, False) tx_removals, tx_additions = tx_removals_and_additions( npc_result.npc_list) else: tx_removals, tx_additions = [], [] assert block.get_included_reward_coins( ) == should_be_included_prev await coin_store.new_block(block, tx_additions, tx_removals) if block.height != 0: with pytest.raises(Exception): await coin_store.new_block(block, tx_additions, tx_removals) for expected_coin in should_be_included_prev: # Check that the coinbase rewards are added record = await coin_store.get_coin_record( expected_coin.name()) assert record is not None assert not record.spent assert record.coin == expected_coin for coin_name in tx_removals: # Check that the removed coins are set to spent record = await coin_store.get_coin_record(coin_name) assert record.spent for coin in tx_additions: # Check that the added coins are added record = await coin_store.get_coin_record(coin.name()) assert not record.spent assert coin == record.coin should_be_included_prev = should_be_included.copy() should_be_included = set() await connection.close() Path("fndb_test.db").unlink()
async def test_block_store(self): assert sqlite3.threadsafety == 1 blocks = bt.get_consecutive_blocks(10) db_filename = Path("blockchain_test.db") db_filename_2 = Path("blockchain_test2.db") if db_filename.exists(): db_filename.unlink() if db_filename_2.exists(): db_filename_2.unlink() connection = await aiosqlite.connect(db_filename) connection_2 = await aiosqlite.connect(db_filename_2) db_wrapper = DBWrapper(connection) db_wrapper_2 = DBWrapper(connection_2) # Use a different file for the blockchain coin_store_2 = await CoinStore.create(db_wrapper_2) store_2 = await BlockStore.create(db_wrapper_2) bc = await Blockchain.create(coin_store_2, store_2, test_constants) store = await BlockStore.create(db_wrapper) await BlockStore.create(db_wrapper_2) try: # Save/get block for block in blocks: await bc.receive_block(block) block_record = bc.block_record(block.header_hash) block_record_hh = block_record.header_hash await store.add_full_block(block.header_hash, block, block_record) await store.add_full_block(block.header_hash, block, block_record) assert block == await store.get_full_block(block.header_hash) assert block == await store.get_full_block(block.header_hash) assert block_record == ( await store.get_block_record(block_record_hh)) await store.set_peak(block_record.header_hash) await store.set_peak(block_record.header_hash) assert len(await store.get_full_blocks_at([1])) == 1 assert len(await store.get_full_blocks_at([0])) == 1 assert len(await store.get_full_blocks_at([100])) == 0 # Get blocks block_record_records = await store.get_block_records_in_range( 0, 0xFFFFFFFF) assert len(block_record_records) == len(blocks) except Exception: await connection.close() await connection_2.close() db_filename.unlink() db_filename_2.unlink() raise await connection.close() await connection_2.close() db_filename.unlink() db_filename_2.unlink()
async def test_stealing_fee(self, two_nodes): receiver_puzzlehash = BURN_PUZZLE_HASH num_blocks = 2 wallet_receiver = bt.get_farmer_wallet_tool() blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10, b"") blocks = bt.get_consecutive_blocks(test_constants, num_blocks, blocks, 10, b"") full_node_1, full_node_2, server_1, server_2 = two_nodes block = blocks[1] wallet_2_block = blocks[3] for b in blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(b) ): pass cvp = ConditionVarPair( ConditionOpcode.ASSERT_FEE, int_to_bytes(10), None, ) dic = {cvp.opcode: [cvp]} fee = 9 spend_bundle1 = generate_test_spend_bundle(block.get_coinbase(), dic, fee) wallet_2_fees = wallet_2_block.get_fees_coin() steal_fee_spendbundle = wallet_receiver.generate_signed_transaction( wallet_2_fees.amount + fee - 4, receiver_puzzlehash, wallet_2_fees ) assert spend_bundle1 is not None assert steal_fee_spendbundle is not None combined = SpendBundle.aggregate([spend_bundle1, steal_fee_spendbundle]) assert combined.fees() == 4 tx1: full_node_protocol.RespondTransaction = ( full_node_protocol.RespondTransaction(spend_bundle1) ) outbound_messages: List[OutboundMessage] = [] async for outbound in full_node_1.respond_transaction(tx1): outbound_messages.append(outbound) new_transaction = False for msg in outbound_messages: if msg.message.function == "new_transaction": new_transaction = True assert new_transaction is False mempool_bundle = full_node_1.mempool_manager.get_spendbundle( spend_bundle1.name() ) assert mempool_bundle is None
async def test_invalid_filter(self, two_nodes): num_blocks = 10 wallet_a = WALLET_A coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] receiver_puzzlehash = BURN_PUZZLE_HASH blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash) full_node_1, full_node_2, server_1, server_2 = two_nodes for block in blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block)): pass spent_block = blocks[1] spend_bundle = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, spent_block.get_coinbase()) assert spend_bundle is not None tx: full_node_protocol.RespondTransaction = ( full_node_protocol.RespondTransaction(spend_bundle)) async for _ in full_node_1.respond_transaction(tx): outbound: OutboundMessage = _ # Maybe transaction means that it's accepted in mempool assert outbound.message.function == "new_transaction" sb = full_node_1.mempool_manager.get_spendbundle(spend_bundle.name()) assert sb is spend_bundle last_block = blocks[10] next_spendbundle = await full_node_1.mempool_manager.create_bundle_for_tip( last_block.header) assert next_spendbundle is not None program = best_solution_program(next_spendbundle) aggsig = next_spendbundle.aggregated_signature dic_h = {11: (program, aggsig)} new_blocks = bt.get_consecutive_blocks(test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h) next_block = new_blocks[11] bad_header = HeaderData( next_block.header.data.height, next_block.header.data.prev_header_hash, next_block.header.data.timestamp, bytes32(bytes([3] * 32)), next_block.header.data.proof_of_space_hash, next_block.header.data.weight, next_block.header.data.total_iters, next_block.header.data.additions_root, next_block.header.data.removals_root, next_block.header.data.farmer_rewards_puzzle_hash, next_block.header.data.total_transaction_fees, next_block.header.data.pool_target, next_block.header.data.aggregated_signature, next_block.header.data.cost, next_block.header.data.extension_data, next_block.header.data.generator_hash, ) bad_block = FullBlock( next_block.proof_of_space, next_block.proof_of_time, Header( bad_header, bt.get_plot_signature( bad_header, next_block.proof_of_space.plot_public_key), ), next_block.transactions_generator, next_block.transactions_filter, ) result, removed, error_code = await full_node_1.blockchain.receive_block( bad_block) assert result == ReceiveBlockResult.INVALID_BLOCK assert error_code == Err.INVALID_TRANSACTIONS_FILTER_HASH result, removed, error_code = await full_node_1.blockchain.receive_block( next_block) assert result == ReceiveBlockResult.ADDED_TO_HEAD
async def test_short_sync_with_transactions_wallet(self, wallet_node): BURN_PUZZLE_HASH_1 = b"0" * 32 BURN_PUZZLE_HASH_2 = b"1" * 32 full_node_1, wallet_node, server_1, server_2 = wallet_node wallet_a = wallet_node.wallet_state_manager.main_wallet coinbase_puzzlehash = await wallet_a.get_new_puzzlehash() coinbase_puzzlehash_rest = BURN_PUZZLE_HASH_1 puzzle_hashes = [ await wallet_a.get_new_puzzlehash() for _ in range(10) ] puzzle_hashes.append(BURN_PUZZLE_HASH_2) blocks = bt.get_consecutive_blocks(test_constants, 3, [], 10, b"", coinbase_puzzlehash) for block in blocks: [ _ async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block)) ] await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) await time_out_assert(60, wallet_height_at_least, True, wallet_node, 1) server_2.global_connections.close_all_connections() dic_h = {} prev_coin = blocks[1].get_coinbase() for i in range(11): pk, sk = await wallet_a.wallet_state_manager.get_keys( prev_coin.puzzle_hash) transaction_unsigned = await wallet_a.generate_unsigned_transaction( 1000, puzzle_hashes[i], coins=[prev_coin]) spend_bundle = await wallet_a.sign_transaction(transaction_unsigned ) block_spendbundle = SpendBundle.aggregate([spend_bundle]) program = best_solution_program(block_spendbundle) aggsig = block_spendbundle.aggregated_signature prev_coin = Coin(prev_coin.name(), puzzle_hashes[i], uint64(1000)) dic_h[i + 4] = (program, aggsig) blocks = bt.get_consecutive_blocks(test_constants, 13, blocks, 10, b"", coinbase_puzzlehash_rest, dic_h) # Move chain to height 16, with consecutive transactions in blocks 4 to 14 for block in blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block)): pass # Do a short sync from 0 to 14 await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) await time_out_assert(60, wallet_height_at_least, True, wallet_node, 14) server_2.global_connections.close_all_connections() # 3 block rewards and 3 fees - 1000 coins spent assert (await wallet_a.get_confirmed_balance() == (blocks[1].get_coinbase().amount * 3) + (blocks[1].get_fees_coin().amount * 3) - 1000) # All of our coins are spent and puzzle hashes present for puzzle_hash in puzzle_hashes[:-1]: records = await wallet_node.wallet_state_manager.wallet_store.get_coin_records_by_puzzle_hash( puzzle_hash) assert len(records) == 1 assert records[0].spent and not records[0].coinbase # Then do the same but in a reorg chain dic_h = {} prev_coin = blocks[1].get_coinbase() for i in range(11): pk, sk = await wallet_a.wallet_state_manager.get_keys( prev_coin.puzzle_hash) transaction_unsigned = await wallet_a.generate_unsigned_transaction( 1000, puzzle_hashes[i], coins=[prev_coin], ) spend_bundle = await wallet_a.sign_transaction(transaction_unsigned ) block_spendbundle = SpendBundle.aggregate([spend_bundle]) program = best_solution_program(block_spendbundle) aggsig = block_spendbundle.aggregated_signature prev_coin = Coin(prev_coin.name(), puzzle_hashes[i], uint64(1000)) dic_h[i + 4] = (program, aggsig) blocks = bt.get_consecutive_blocks( test_constants, 31, blocks[:4], 10, b"this is a reorg", coinbase_puzzlehash_rest, dic_h, ) # Move chain to height 34, with consecutive transactions in blocks 4 to 14 for block in blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block)): pass # Do a sync from 0 to 22 await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) await time_out_assert(60, wallet_height_at_least, True, wallet_node, 28) server_2.global_connections.close_all_connections() # 3 block rewards and 3 fees - 1000 coins spent assert (await wallet_a.get_confirmed_balance() == (blocks[1].get_coinbase().amount * 3) + (blocks[1].get_fees_coin().amount * 3) - 1000) # All of our coins are spent and puzzle hashes present for puzzle_hash in puzzle_hashes[:-1]: records = await wallet_node.wallet_state_manager.wallet_store.get_coin_records_by_puzzle_hash( puzzle_hash) assert len(records) == 1 assert records[0].spent and not records[0].coinbase # Test spending the rewards earned in reorg new_coinbase_puzzlehash = await wallet_a.get_new_puzzlehash() another_puzzlehash = await wallet_a.get_new_puzzlehash() dic_h = {} pk, sk = await wallet_a.wallet_state_manager.get_keys( new_coinbase_puzzlehash) coinbase_coin = create_coinbase_coin(uint32(25), new_coinbase_puzzlehash, uint64(14000000000000)) transaction_unsigned = await wallet_a.generate_unsigned_transaction( 7000000000000, another_puzzlehash, coins=[coinbase_coin], ) spend_bundle = await wallet_a.sign_transaction(transaction_unsigned) block_spendbundle = SpendBundle.aggregate([spend_bundle]) program = best_solution_program(block_spendbundle) aggsig = block_spendbundle.aggregated_signature dic_h[26] = (program, aggsig) # Farm a block (25) to ourselves blocks = bt.get_consecutive_blocks( test_constants, 1, blocks[:25], 10, b"this is yet another reorg", new_coinbase_puzzlehash, ) # Brings height up to 40, with block 31 having half our reward spent to us blocks = bt.get_consecutive_blocks( test_constants, 15, blocks, 10, b"this is yet another reorg more blocks", coinbase_puzzlehash_rest, dic_h, ) for block in blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block)): pass await server_2.start_client( PeerInfo("localhost", uint16(server_1._port)), None) await time_out_assert(60, wallet_height_at_least, True, wallet_node, 38) # 4 block rewards and 4 fees - 1000 coins spent assert (await wallet_a.get_confirmed_balance() == (blocks[1].get_coinbase().amount * 4) + (blocks[1].get_fees_coin().amount * 4) - 1000) records = await wallet_node.wallet_state_manager.wallet_store.get_coin_records_by_puzzle_hash( new_coinbase_puzzlehash) # Fee and coinbase assert len(records) == 2 print(records) assert records[0].spent != records[1].spent assert records[0].coinbase == records[1].coinbase records = await wallet_node.wallet_state_manager.wallet_store.get_coin_records_by_puzzle_hash( another_puzzlehash) assert len(records) == 1 assert not records[0].spent assert not records[0].coinbase
async def test_basic_blockchain_tx(self, two_nodes): num_blocks = 10 wallet_a = WALLET_A coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] receiver_puzzlehash = BURN_PUZZLE_HASH blocks = bt.get_consecutive_blocks( num_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True) full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes peer = await connect_and_get_peer(server_1, server_2) full_node_1 = full_node_api_1.full_node for block in blocks: await full_node_api_1.full_node.respond_block( full_node_protocol.RespondBlock(block), None) spend_block = blocks[2] spend_coin = None for coin in list(spend_block.get_included_reward_coins()): if coin.puzzle_hash == coinbase_puzzlehash: spend_coin = coin spend_bundle = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, spend_coin) assert spend_bundle is not None tx: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction( spend_bundle) await full_node_api_1.respond_transaction(tx, peer) sb = full_node_1.mempool_manager.get_spendbundle(spend_bundle.name()) assert sb is spend_bundle last_block = blocks[-1] next_spendbundle, additions, removals = await full_node_1.mempool_manager.create_bundle_from_mempool( last_block.header_hash) assert next_spendbundle is not None new_blocks = bt.get_consecutive_blocks( 1, block_list_input=blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, transaction_data=next_spendbundle, guarantee_transaction_block=True, ) next_block = new_blocks[-1] await full_node_1.respond_block( full_node_protocol.RespondBlock(next_block)) assert next_block.header_hash == full_node_1.blockchain.get_peak( ).header_hash added_coins = next_spendbundle.additions() # Two coins are added, main spend and change assert len(added_coins) == 2 for coin in added_coins: unspent = await full_node_1.coin_store.get_coin_record(coin.name()) assert unspent is not None assert not unspent.spent assert not unspent.coinbase
async def test1(self, two_nodes): num_blocks = 5 test_rpc_port = uint16(21522) full_node_1, full_node_2, server_1, server_2 = two_nodes blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10) for i in range(1, num_blocks): async for _ in full_node_1.respond_unfinished_block( full_node_protocol.RespondUnfinishedBlock(blocks[i])): pass async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(blocks[i])): pass def stop_node_cb(): full_node_1._close() server_1.close_all() full_node_rpc_api = FullNodeRpcApi(full_node_1) config = load_config(bt.root_path, "config.yaml") hostname = config["self_hostname"] daemon_port = config["daemon_port"] rpc_cleanup = await start_rpc_server( full_node_rpc_api, hostname, daemon_port, test_rpc_port, stop_node_cb, connect_to_daemon=False, ) try: client = await FullNodeRpcClient.create("localhost", test_rpc_port) state = await client.get_blockchain_state() assert state["lca"].header_hash is not None assert not state["sync"]["sync_mode"] assert len(state["tips"]) > 0 assert state["difficulty"] > 0 assert state["ips"] > 0 assert state["min_iters"] > 0 block = await client.get_block(state["lca"].header_hash) assert block == blocks[2] assert (await client.get_block(bytes([1] * 32))) is None unf_block_headers = await client.get_unfinished_block_headers(4) assert len(unf_block_headers) == 1 assert unf_block_headers[0] == blocks[4].header header = await client.get_header(state["lca"].header_hash) assert header == blocks[2].header assert (await client.get_header_by_height(2)) == blocks[2].header assert (await client.get_header_by_height(100)) is None coins = await client.get_unspent_coins( blocks[-1].get_coinbase().puzzle_hash, blocks[-1].header_hash) assert len(coins) == 3 coins_lca = await client.get_unspent_coins( blocks[-1].get_coinbase().puzzle_hash) assert len(coins_lca) == 3 assert len(await client.get_connections()) == 0 await client.open_connection("localhost", server_2._port) async def num_connections(): return len(await client.get_connections()) await time_out_assert(10, num_connections, 1) connections = await client.get_connections() await client.close_connection(connections[0]["node_id"]) await time_out_assert(10, num_connections, 0) except AssertionError: # 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()
async def test_assert_time_exceeds(self, two_nodes): num_blocks = 10 wallet_a = WALLET_A coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] receiver_puzzlehash = BURN_PUZZLE_HASH # Farm blocks blocks = bt.get_consecutive_blocks( num_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True) full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes full_node_1 = full_node_api_1.full_node for block in blocks: await full_node_api_1.full_node.respond_block( full_node_protocol.RespondBlock(block)) # Coinbase that gets spent block1 = blocks[2] spend_coin_block_1 = None for coin in list(block1.get_included_reward_coins()): if coin.puzzle_hash == coinbase_puzzlehash: spend_coin_block_1 = coin # This condition requires block1 coinbase to be spent after 30 seconds from now current_time_plus3 = uint64( blocks[-1].foliage_transaction_block.timestamp + 30) block1_cvp = ConditionVarPair( ConditionOpcode.ASSERT_SECONDS_NOW_EXCEEDS, [int_to_bytes(current_time_plus3)]) block1_dic = {block1_cvp.opcode: [block1_cvp]} block1_spend_bundle = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, spend_coin_block_1, block1_dic) # program that will be sent to early assert block1_spend_bundle is not None invalid_new_blocks = bt.get_consecutive_blocks( 1, blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, transaction_data=block1_spend_bundle, time_per_block=20, guarantee_transaction_block=True, ) # Try to validate that block before 3 sec res, err, _ = await full_node_1.blockchain.receive_block( invalid_new_blocks[-1]) assert res == ReceiveBlockResult.INVALID_BLOCK assert err == Err.ASSERT_SECONDS_NOW_EXCEEDS_FAILED valid_new_blocks = bt.get_consecutive_blocks( 1, blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, transaction_data=block1_spend_bundle, guarantee_transaction_block=True, time_per_block=31, ) res, err, _ = await full_node_1.blockchain.receive_block( valid_new_blocks[-1]) assert err is None assert res == ReceiveBlockResult.NEW_PEAK
async def test_basic_store(self, empty_blockchain, normalized_to_identity: bool = False): blockchain = empty_blockchain blocks = bt.get_consecutive_blocks( 10, seed=b"1234", normalized_to_identity_cc_eos=normalized_to_identity, normalized_to_identity_icc_eos=normalized_to_identity, normalized_to_identity_cc_ip=normalized_to_identity, normalized_to_identity_cc_sp=normalized_to_identity, ) store = await FullNodeStore.create(test_constants) unfinished_blocks = [] for block in blocks: unfinished_blocks.append( UnfinishedBlock( block.finished_sub_slots, block.reward_chain_block.get_unfinished(), block.challenge_chain_sp_proof, block.reward_chain_sp_proof, block.foliage, block.foliage_transaction_block, block.transactions_info, block.transactions_generator, [], )) # Add/get candidate block assert store.get_candidate_block( unfinished_blocks[0].get_hash()) is None for height, unf_block in enumerate(unfinished_blocks): store.add_candidate_block(unf_block.get_hash(), height, unf_block) assert store.get_candidate_block( unfinished_blocks[4].get_hash())[1] == unfinished_blocks[4] store.clear_candidate_blocks_below(uint32(8)) assert store.get_candidate_block( unfinished_blocks[5].get_hash()) is None assert store.get_candidate_block( unfinished_blocks[8].get_hash()) is not None # Test seen unfinished blocks h_hash_1 = bytes32(token_bytes(32)) assert not store.seen_unfinished_block(h_hash_1) assert store.seen_unfinished_block(h_hash_1) store.clear_seen_unfinished_blocks() assert not store.seen_unfinished_block(h_hash_1) # Add/get unfinished block for height, unf_block in enumerate(unfinished_blocks): assert store.get_unfinished_block(unf_block.partial_hash) is None store.add_unfinished_block( height, unf_block, PreValidationResult(None, uint64(123532), None)) assert store.get_unfinished_block( unf_block.partial_hash) == unf_block store.remove_unfinished_block(unf_block.partial_hash) assert store.get_unfinished_block(unf_block.partial_hash) is None blocks = bt.get_consecutive_blocks( 1, skip_slots=5, normalized_to_identity_cc_ip=normalized_to_identity, normalized_to_identity_cc_sp=normalized_to_identity, normalized_to_identity_cc_eos=normalized_to_identity, normalized_to_identity_icc_eos=normalized_to_identity, ) sub_slots = blocks[0].finished_sub_slots assert len(sub_slots) == 5 assert (store.get_finished_sub_slots( BlockCache({}), None, sub_slots[0].challenge_chain.challenge_chain_end_of_slot_vdf. challenge, ) == []) # Test adding non-connecting sub-slots genesis assert store.get_sub_slot(test_constants.GENESIS_CHALLENGE) is None assert store.get_sub_slot( sub_slots[0].challenge_chain.get_hash()) is None assert store.get_sub_slot( sub_slots[1].challenge_chain.get_hash()) is None assert store.new_finished_sub_slot(sub_slots[1], {}, None, None) is None assert store.new_finished_sub_slot(sub_slots[2], {}, None, None) is None # Test adding sub-slots after genesis assert store.new_finished_sub_slot(sub_slots[0], {}, None, None) is not None assert store.get_sub_slot( sub_slots[0].challenge_chain.get_hash())[0] == sub_slots[0] assert store.get_sub_slot( sub_slots[1].challenge_chain.get_hash()) is None assert store.new_finished_sub_slot(sub_slots[1], {}, None, None) is not None for i in range(len(sub_slots)): assert store.new_finished_sub_slot(sub_slots[i], {}, None, None) is not None assert store.get_sub_slot( sub_slots[i].challenge_chain.get_hash())[0] == sub_slots[i] assert store.get_finished_sub_slots( BlockCache({}), None, sub_slots[-1].challenge_chain.get_hash()) == sub_slots assert store.get_finished_sub_slots(BlockCache( {}), None, std_hash(b"not a valid hash")) is None assert (store.get_finished_sub_slots( BlockCache({}), None, sub_slots[-2].challenge_chain.get_hash()) == sub_slots[:-1]) # Test adding genesis peak await blockchain.receive_block(blocks[0]) peak = blockchain.get_peak() peak_full_block = await blockchain.get_full_peak() if peak.overflow: store.new_peak(peak, peak_full_block, sub_slots[-2], sub_slots[-1], False, {}) else: store.new_peak(peak, peak_full_block, None, sub_slots[-1], False, {}) assert store.get_sub_slot( sub_slots[0].challenge_chain.get_hash()) is None assert store.get_sub_slot( sub_slots[1].challenge_chain.get_hash()) is None assert store.get_sub_slot( sub_slots[2].challenge_chain.get_hash()) is None if peak.overflow: assert store.get_sub_slot( sub_slots[3].challenge_chain.get_hash())[0] == sub_slots[3] else: assert store.get_sub_slot( sub_slots[3].challenge_chain.get_hash()) is None assert store.get_sub_slot( sub_slots[4].challenge_chain.get_hash())[0] == sub_slots[4] assert (store.get_finished_sub_slots( blockchain, peak, sub_slots[-1].challenge_chain.get_hash(), ) == []) # Test adding non genesis peak directly blocks = bt.get_consecutive_blocks( 2, skip_slots=2, normalized_to_identity_cc_eos=normalized_to_identity, normalized_to_identity_icc_eos=normalized_to_identity, normalized_to_identity_cc_ip=normalized_to_identity, normalized_to_identity_cc_sp=normalized_to_identity, ) blocks = bt.get_consecutive_blocks( 3, block_list_input=blocks, normalized_to_identity_cc_eos=normalized_to_identity, normalized_to_identity_icc_eos=normalized_to_identity, normalized_to_identity_cc_ip=normalized_to_identity, normalized_to_identity_cc_sp=normalized_to_identity, ) for block in blocks: await blockchain.receive_block(block) sb = blockchain.block_record(block.header_hash) sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots( block.header_hash) res = store.new_peak(sb, block, sp_sub_slot, ip_sub_slot, False, blockchain) assert res[0] is None # Add reorg blocks blocks_reorg = bt.get_consecutive_blocks( 20, normalized_to_identity_cc_eos=normalized_to_identity, normalized_to_identity_icc_eos=normalized_to_identity, normalized_to_identity_cc_ip=normalized_to_identity, normalized_to_identity_cc_sp=normalized_to_identity, ) for block in blocks_reorg: res, _, _ = await blockchain.receive_block(block) if res == ReceiveBlockResult.NEW_PEAK: sb = blockchain.block_record(block.header_hash) sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots( block.header_hash) res = store.new_peak(sb, block, sp_sub_slot, ip_sub_slot, True, blockchain) assert res[0] is None # Add slots to the end blocks_2 = bt.get_consecutive_blocks( 1, block_list_input=blocks_reorg, skip_slots=2, normalized_to_identity_cc_eos=normalized_to_identity, normalized_to_identity_icc_eos=normalized_to_identity, normalized_to_identity_cc_ip=normalized_to_identity, normalized_to_identity_cc_sp=normalized_to_identity, ) for slot in blocks_2[-1].finished_sub_slots: store.new_finished_sub_slot(slot, blockchain, blockchain.get_peak(), await blockchain.get_full_peak()) assert store.get_sub_slot( sub_slots[3].challenge_chain.get_hash()) is None assert store.get_sub_slot( sub_slots[4].challenge_chain.get_hash()) is None # Test adding signage point peak = blockchain.get_peak() ss_start_iters = peak.ip_sub_slot_total_iters(test_constants) for i in range( 1, test_constants.NUM_SPS_SUB_SLOT - test_constants.NUM_SP_INTERVALS_EXTRA): sp = get_signage_point( test_constants, blockchain, peak, ss_start_iters, uint8(i), [], peak.sub_slot_iters, ) assert store.new_signage_point(i, blockchain, peak, peak.sub_slot_iters, sp) blocks = blocks_reorg while True: blocks = bt.get_consecutive_blocks( 1, block_list_input=blocks, normalized_to_identity_cc_eos=normalized_to_identity, normalized_to_identity_icc_eos=normalized_to_identity, normalized_to_identity_cc_ip=normalized_to_identity, normalized_to_identity_cc_sp=normalized_to_identity, ) res, _, _ = await blockchain.receive_block(blocks[-1]) if res == ReceiveBlockResult.NEW_PEAK: sb = blockchain.block_record(blocks[-1].header_hash) sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots( blocks[-1].header_hash) res = store.new_peak(sb, blocks[-1], sp_sub_slot, ip_sub_slot, True, blockchain) assert res[0] is None if sb.overflow and sp_sub_slot is not None: assert sp_sub_slot != ip_sub_slot break peak = blockchain.get_peak() assert peak.overflow # Overflow peak should result in 2 finished sub slots assert len(store.finished_sub_slots) == 2 # Add slots to the end, except for the last one, which we will use to test invalid SP blocks_2 = bt.get_consecutive_blocks( 1, block_list_input=blocks, skip_slots=3, normalized_to_identity_cc_eos=normalized_to_identity, normalized_to_identity_icc_eos=normalized_to_identity, normalized_to_identity_cc_ip=normalized_to_identity, normalized_to_identity_cc_sp=normalized_to_identity, ) for slot in blocks_2[-1].finished_sub_slots[:-1]: store.new_finished_sub_slot(slot, blockchain, blockchain.get_peak(), await blockchain.get_full_peak()) finished_sub_slots = blocks_2[-1].finished_sub_slots assert len(store.finished_sub_slots) == 4 # Test adding signage points for overflow blocks (sp_sub_slot) ss_start_iters = peak.sp_sub_slot_total_iters(test_constants) # for i in range(peak.signage_point_index, test_constants.NUM_SPS_SUB_SLOT): # if i < peak.signage_point_index: # continue # latest = peak # while latest.total_iters > peak.sp_total_iters(test_constants): # latest = blockchain.blocks[latest.prev_hash] # sp = get_signage_point( # test_constants, # blockchain.blocks, # latest, # ss_start_iters, # uint8(i), # [], # peak.sub_slot_iters, # ) # assert store.new_signage_point(i, blockchain.blocks, peak, peak.sub_slot_iters, sp) # Test adding signage points for overflow blocks (ip_sub_slot) for i in range( 1, test_constants.NUM_SPS_SUB_SLOT - test_constants.NUM_SP_INTERVALS_EXTRA): sp = get_signage_point( test_constants, blockchain, peak, peak.ip_sub_slot_total_iters(test_constants), uint8(i), [], peak.sub_slot_iters, ) assert store.new_signage_point(i, blockchain, peak, peak.sub_slot_iters, sp) # Test adding future signage point, a few slots forward (good) saved_sp_hash = None for slot_offset in range(1, len(finished_sub_slots)): for i in range( 1, test_constants.NUM_SPS_SUB_SLOT - test_constants.NUM_SP_INTERVALS_EXTRA, ): sp = get_signage_point( test_constants, blockchain, peak, peak.ip_sub_slot_total_iters(test_constants) + slot_offset * peak.sub_slot_iters, uint8(i), finished_sub_slots[:slot_offset], peak.sub_slot_iters, ) assert sp.cc_vdf is not None saved_sp_hash = sp.cc_vdf.output.get_hash() assert store.new_signage_point(i, blockchain, peak, peak.sub_slot_iters, sp) # Test adding future signage point (bad) for i in range( 1, test_constants.NUM_SPS_SUB_SLOT - test_constants.NUM_SP_INTERVALS_EXTRA): sp = get_signage_point( test_constants, blockchain, peak, peak.ip_sub_slot_total_iters(test_constants) + len(finished_sub_slots) * peak.sub_slot_iters, uint8(i), finished_sub_slots[:len(finished_sub_slots)], peak.sub_slot_iters, ) assert not store.new_signage_point(i, blockchain, peak, peak.sub_slot_iters, sp) # Test adding past signage point sp = SignagePoint( blocks[1].reward_chain_block.challenge_chain_sp_vdf, blocks[1].challenge_chain_sp_proof, blocks[1].reward_chain_block.reward_chain_sp_vdf, blocks[1].reward_chain_sp_proof, ) assert not store.new_signage_point( blocks[1].reward_chain_block.signage_point_index, {}, peak, blockchain.block_record( blocks[1].header_hash).sp_sub_slot_total_iters(test_constants), sp, ) # Get signage point by index assert (store.get_signage_point_by_index( finished_sub_slots[0].challenge_chain.get_hash(), 4, finished_sub_slots[0].reward_chain.get_hash(), ) is not None) assert (store.get_signage_point_by_index( finished_sub_slots[0].challenge_chain.get_hash(), 4, std_hash(b"1")) is None) # Get signage point by hash assert store.get_signage_point(saved_sp_hash) is not None assert store.get_signage_point(std_hash(b"2")) is None # Test adding signage points before genesis store.initialize_genesis_sub_slot() assert len(store.finished_sub_slots) == 1 for i in range( 1, test_constants.NUM_SPS_SUB_SLOT - test_constants.NUM_SP_INTERVALS_EXTRA): sp = get_signage_point( test_constants, BlockCache({}, {}), None, uint128(0), uint8(i), [], peak.sub_slot_iters, ) assert store.new_signage_point(i, {}, None, peak.sub_slot_iters, sp) blocks_3 = bt.get_consecutive_blocks( 1, skip_slots=2, normalized_to_identity_cc_eos=normalized_to_identity, normalized_to_identity_icc_eos=normalized_to_identity, normalized_to_identity_cc_ip=normalized_to_identity, normalized_to_identity_cc_sp=normalized_to_identity, ) for slot in blocks_3[-1].finished_sub_slots: store.new_finished_sub_slot(slot, {}, None, None) assert len(store.finished_sub_slots) == 3 finished_sub_slots = blocks_3[-1].finished_sub_slots for slot_offset in range(1, len(finished_sub_slots) + 1): for i in range( 1, test_constants.NUM_SPS_SUB_SLOT - test_constants.NUM_SP_INTERVALS_EXTRA, ): sp = get_signage_point( test_constants, BlockCache({}, {}), None, slot_offset * peak.sub_slot_iters, uint8(i), finished_sub_slots[:slot_offset], peak.sub_slot_iters, ) assert store.new_signage_point(i, {}, None, peak.sub_slot_iters, sp) # Test adding signage points after genesis blocks_4 = bt.get_consecutive_blocks( 1, normalized_to_identity_cc_eos=normalized_to_identity, normalized_to_identity_icc_eos=normalized_to_identity, normalized_to_identity_cc_ip=normalized_to_identity, normalized_to_identity_cc_sp=normalized_to_identity, ) blocks_5 = bt.get_consecutive_blocks( 1, block_list_input=blocks_4, skip_slots=1, normalized_to_identity_cc_eos=normalized_to_identity, normalized_to_identity_icc_eos=normalized_to_identity, normalized_to_identity_cc_ip=normalized_to_identity, normalized_to_identity_cc_sp=normalized_to_identity, ) # If this is not the case, fix test to find a block that is assert (blocks_4[-1].reward_chain_block.signage_point_index < test_constants.NUM_SPS_SUB_SLOT - test_constants.NUM_SP_INTERVALS_EXTRA) await blockchain.receive_block(blocks_4[-1]) sb = blockchain.block_record(blocks_4[-1].header_hash) store.new_peak(sb, blocks_4[-1], None, None, False, blockchain) for i in range( sb.signage_point_index + test_constants.NUM_SP_INTERVALS_EXTRA, test_constants.NUM_SPS_SUB_SLOT, ): if is_overflow_block(test_constants, uint8(i)): finished_sub_slots = blocks_5[-1].finished_sub_slots else: finished_sub_slots = [] sp = get_signage_point( test_constants, blockchain, sb, uint128(0), uint8(i), finished_sub_slots, peak.sub_slot_iters, ) assert store.new_signage_point(i, empty_blockchain, sb, peak.sub_slot_iters, sp) # Test future EOS cache store.initialize_genesis_sub_slot() blocks = bt.get_consecutive_blocks( 1, normalized_to_identity_cc_eos=normalized_to_identity, normalized_to_identity_icc_eos=normalized_to_identity, normalized_to_identity_cc_ip=normalized_to_identity, normalized_to_identity_cc_sp=normalized_to_identity, ) await blockchain.receive_block(blocks[-1]) while True: blocks = bt.get_consecutive_blocks( 1, block_list_input=blocks, normalized_to_identity_cc_eos=normalized_to_identity, normalized_to_identity_icc_eos=normalized_to_identity, normalized_to_identity_cc_ip=normalized_to_identity, normalized_to_identity_cc_sp=normalized_to_identity, ) await blockchain.receive_block(blocks[-1]) sb = blockchain.block_record(blocks[-1].header_hash) if sb.first_in_sub_slot: break assert len(blocks) >= 2 dependant_sub_slots = blocks[-1].finished_sub_slots peak = blockchain.get_peak() peak_full_block = await blockchain.get_full_peak() for block in blocks[:-2]: sb = blockchain.block_record(block.header_hash) sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots( block.header_hash) peak = sb peak_full_block = block res = store.new_peak(sb, block, sp_sub_slot, ip_sub_slot, False, blockchain) assert res[0] is None assert store.new_finished_sub_slot(dependant_sub_slots[0], blockchain, peak, peak_full_block) is None block = blocks[-2] sb = blockchain.block_record(block.header_hash) sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots( block.header_hash) res = store.new_peak(sb, block, sp_sub_slot, ip_sub_slot, False, blockchain) assert res[0] == dependant_sub_slots[0] assert res[1] == res[2] == [] # Test future IP cache store.initialize_genesis_sub_slot() blocks = bt.get_consecutive_blocks( 60, normalized_to_identity_cc_ip=normalized_to_identity, normalized_to_identity_cc_sp=normalized_to_identity, normalized_to_identity_cc_eos=normalized_to_identity, normalized_to_identity_icc_eos=normalized_to_identity, ) for block in blocks[:5]: await blockchain.receive_block(block) sb = blockchain.block_record(block.header_hash) sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots( block.header_hash) res = store.new_peak(sb, block, sp_sub_slot, ip_sub_slot, False, blockchain) assert res[0] is None case_0, case_1 = False, False for i in range(5, len(blocks) - 1): prev_block = blocks[i] block = blocks[i + 1] new_ip = NewInfusionPointVDF( block.reward_chain_block.get_unfinished().get_hash(), block.reward_chain_block.challenge_chain_ip_vdf, block.challenge_chain_ip_proof, block.reward_chain_block.reward_chain_ip_vdf, block.reward_chain_ip_proof, block.reward_chain_block.infused_challenge_chain_ip_vdf, block.infused_challenge_chain_ip_proof, ) store.add_to_future_ip(new_ip) await blockchain.receive_block(prev_block) sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots( prev_block.header_hash) sb = blockchain.block_record(prev_block.header_hash) res = store.new_peak(sb, prev_block, sp_sub_slot, ip_sub_slot, False, blockchain) if len(block.finished_sub_slots) == 0: case_0 = True assert res[2] == [new_ip] else: case_1 = True assert res[2] == [] found_ips = [] for ss in block.finished_sub_slots: found_ips += store.new_finished_sub_slot( ss, blockchain, sb, prev_block) assert found_ips == [new_ip] # If flaky, increase the number of blocks created assert case_0 and case_1
async def test_long_sync_from_zero(self, five_nodes, default_400_blocks): # Must be larger than "sync_block_behind_threshold" in the config num_blocks = len(default_400_blocks) blocks: List[FullBlock] = default_400_blocks full_node_1, full_node_2, full_node_3, full_node_4, full_node_5 = five_nodes server_1 = full_node_1.full_node.server server_2 = full_node_2.full_node.server server_3 = full_node_3.full_node.server server_4 = full_node_4.full_node.server server_5 = full_node_5.full_node.server # If this constant is changed, update the tests to use more blocks assert test_constants.WEIGHT_PROOF_RECENT_BLOCKS < 400 # Syncs up less than recent blocks for block in blocks[:test_constants.WEIGHT_PROOF_RECENT_BLOCKS - 5]: await full_node_1.full_node.respond_block( full_node_protocol.RespondBlock(block)) await server_2.start_client( PeerInfo(self_hostname, uint16(server_1._port)), on_connect=full_node_2.full_node.on_connect) # The second node should eventually catch up to the first one await time_out_assert( 150, node_height_exactly, True, full_node_2, test_constants.WEIGHT_PROOF_RECENT_BLOCKS - 5 - 1) for block in blocks[test_constants.WEIGHT_PROOF_RECENT_BLOCKS - 5:test_constants.WEIGHT_PROOF_RECENT_BLOCKS + 5]: await full_node_1.full_node.respond_block( full_node_protocol.RespondBlock(block)) await server_3.start_client( PeerInfo(self_hostname, uint16(server_1._port)), on_connect=full_node_3.full_node.on_connect) timeout_seconds = 150 # Node 3 and Node 2 sync up to node 1 await time_out_assert( timeout_seconds, node_height_exactly, True, full_node_2, test_constants.WEIGHT_PROOF_RECENT_BLOCKS + 5 - 1) await time_out_assert( timeout_seconds, node_height_exactly, True, full_node_3, test_constants.WEIGHT_PROOF_RECENT_BLOCKS + 5 - 1) cons = list(server_1.all_connections.values())[:] for con in cons: await con.close() for block in blocks[test_constants.WEIGHT_PROOF_RECENT_BLOCKS + 5:]: await full_node_1.full_node.respond_block( full_node_protocol.RespondBlock(block)) await server_2.start_client( PeerInfo(self_hostname, uint16(server_1._port)), on_connect=full_node_2.full_node.on_connect) await server_3.start_client( PeerInfo(self_hostname, uint16(server_1._port)), on_connect=full_node_3.full_node.on_connect) await server_4.start_client( PeerInfo(self_hostname, uint16(server_1._port)), on_connect=full_node_4.full_node.on_connect) await server_3.start_client( PeerInfo(self_hostname, uint16(server_2._port)), on_connect=full_node_3.full_node.on_connect) await server_4.start_client( PeerInfo(self_hostname, uint16(server_3._port)), on_connect=full_node_4.full_node.on_connect) await server_4.start_client( PeerInfo(self_hostname, uint16(server_2._port)), on_connect=full_node_4.full_node.on_connect) # All four nodes are synced await time_out_assert(timeout_seconds, node_height_exactly, True, full_node_1, num_blocks - 1) await time_out_assert(timeout_seconds, node_height_exactly, True, full_node_2, num_blocks - 1) await time_out_assert(timeout_seconds, node_height_exactly, True, full_node_3, num_blocks - 1) await time_out_assert(timeout_seconds, node_height_exactly, True, full_node_4, num_blocks - 1) # Deep reorg, fall back from batch sync to long sync blocks_node_5 = bt.get_consecutive_blocks( 60, block_list_input=blocks[:350], seed=b"node5") for block in blocks_node_5: await full_node_5.full_node.respond_block( full_node_protocol.RespondBlock(block)) await server_5.start_client( PeerInfo(self_hostname, uint16(server_1._port)), on_connect=full_node_5.full_node.on_connect) await time_out_assert(timeout_seconds, node_height_exactly, True, full_node_5, 409) await time_out_assert(timeout_seconds, node_height_exactly, True, full_node_1, 409)
async def test_validate_blockchain_spend_reorg_coin(self, two_nodes): num_blocks = 10 wallet_a = WALLET_A coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] receiver_1_puzzlehash = WALLET_A_PUZZLE_HASHES[1] receiver_2_puzzlehash = WALLET_A_PUZZLE_HASHES[2] receiver_3_puzzlehash = WALLET_A_PUZZLE_HASHES[3] blocks = bt.get_consecutive_blocks( num_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True ) full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes for block in blocks: await full_node_api_1.full_node.respond_block(full_node_protocol.RespondBlock(block)) spend_block = blocks[2] spend_coin = None for coin in list(spend_block.get_included_reward_coins()): if coin.puzzle_hash == coinbase_puzzlehash: spend_coin = coin spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_1_puzzlehash, spend_coin) new_blocks = bt.get_consecutive_blocks( 1, blocks[:5], seed=b"spend_reorg_coin", farmer_reward_puzzle_hash=coinbase_puzzlehash, transaction_data=spend_bundle, guarantee_transaction_block=True, ) await full_node_api_1.full_node.respond_block(full_node_protocol.RespondBlock(new_blocks[-1])) coin_2 = None for coin in new_blocks[-1].additions(): if coin.puzzle_hash == receiver_1_puzzlehash: coin_2 = coin break assert coin_2 is not None spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_2_puzzlehash, coin_2) new_blocks = bt.get_consecutive_blocks( 1, new_blocks[:6], seed=b"spend_reorg_coin", farmer_reward_puzzle_hash=coinbase_puzzlehash, transaction_data=spend_bundle, guarantee_transaction_block=True, ) await full_node_api_1.full_node.respond_block(full_node_protocol.RespondBlock(new_blocks[-1])) coin_3 = None for coin in new_blocks[-1].additions(): if coin.puzzle_hash == receiver_2_puzzlehash: coin_3 = coin break assert coin_3 is not None spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_3_puzzlehash, coin_3) new_blocks = bt.get_consecutive_blocks( 1, new_blocks[:7], seed=b"spend_reorg_coin", farmer_reward_puzzle_hash=coinbase_puzzlehash, transaction_data=spend_bundle, guarantee_transaction_block=True, ) await full_node_api_1.full_node.respond_block(full_node_protocol.RespondBlock(new_blocks[-1])) coin_4 = None for coin in new_blocks[-1].additions(): if coin.puzzle_hash == receiver_3_puzzlehash: coin_4 = coin break assert coin_4 is not None
async def test_full_block_performance(self, wallet_nodes): full_node_1, server_1, wallet_a, wallet_receiver = wallet_nodes blocks = await full_node_1.get_all_full_blocks() full_node_1.full_node.mempool_manager.limit_factor = 1 wallet_ph = wallet_a.get_new_puzzlehash() blocks = bt.get_consecutive_blocks( 10, block_list_input=blocks, guarantee_transaction_block=True, farmer_reward_puzzle_hash=wallet_ph, pool_reward_puzzle_hash=wallet_ph, ) for block in blocks: await full_node_1.full_node.respond_block(fnp.RespondBlock(block)) start_height = (full_node_1.full_node.blockchain.get_peak().height if full_node_1.full_node.blockchain.get_peak() is not None else -1) incoming_queue, node_id = await add_dummy_connection(server_1, 12312) fake_peer = server_1.all_connections[node_id] # Mempool has capacity of 100, make 110 unspents that we can use puzzle_hashes = [] # Makes a bunch of coins for i in range(20): conditions_dict: Dict = {ConditionOpcode.CREATE_COIN: []} # This should fit in one transaction for _ in range(100): receiver_puzzlehash = wallet_receiver.get_new_puzzlehash() puzzle_hashes.append(receiver_puzzlehash) output = ConditionWithArgs( ConditionOpcode.CREATE_COIN, [receiver_puzzlehash, int_to_bytes(100000000)]) conditions_dict[ConditionOpcode.CREATE_COIN].append(output) spend_bundle = wallet_a.generate_signed_transaction( 100, puzzle_hashes[0], get_future_reward_coins(blocks[1 + i])[0], condition_dic=conditions_dict, ) assert spend_bundle is not None respond_transaction_2 = fnp.RespondTransaction(spend_bundle) await full_node_1.respond_transaction(respond_transaction_2, fake_peer) blocks = bt.get_consecutive_blocks( 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=spend_bundle, ) await full_node_1.full_node.respond_block( fnp.RespondBlock(blocks[-1]), fake_peer) await time_out_assert(10, node_height_at_least, True, full_node_1, start_height + 20) spend_bundles = [] spend_bundle_ids = [] # Fill mempool for puzzle_hash in puzzle_hashes[1:]: coin_record = (await full_node_1.full_node.coin_store. get_coin_records_by_puzzle_hash(True, puzzle_hash))[0] receiver_puzzlehash = wallet_receiver.get_new_puzzlehash() if puzzle_hash == puzzle_hashes[-1]: fee = 100000000 # 100 million (20 fee per cost) else: fee = random.randint(1, 100000000) spend_bundle = wallet_receiver.generate_signed_transaction( uint64(500), receiver_puzzlehash, coin_record.coin, fee=fee) spend_bundles.append(spend_bundle) spend_bundle_ids.append(spend_bundle.get_hash()) pr = cProfile.Profile() pr.enable() start = time.time() num_tx: int = 0 for spend_bundle, spend_bundle_id in zip(spend_bundles, spend_bundle_ids): num_tx += 1 respond_transaction = fnp.RespondTransaction(spend_bundle) await full_node_1.respond_transaction(respond_transaction, fake_peer) request = fnp.RequestTransaction(spend_bundle_id) req = await full_node_1.request_transaction(request) if req is None: break log.warning(f"Num Tx: {num_tx}") log.warning(f"Time for mempool: {time.time() - start}") pr.create_stats() pr.dump_stats("./mempool-benchmark.pstats") # Create an unfinished block peak = full_node_1.full_node.blockchain.get_peak() assert peak is not None curr: BlockRecord = peak while not curr.is_transaction_block: curr = full_node_1.full_node.blockchain.block_record( curr.prev_hash) mempool_bundle = await full_node_1.full_node.mempool_manager.create_bundle_from_mempool( curr.header_hash) if mempool_bundle is None: spend_bundle = None else: spend_bundle = mempool_bundle[0] current_blocks = await full_node_1.get_all_full_blocks() blocks = bt.get_consecutive_blocks( 1, transaction_data=spend_bundle, block_list_input=current_blocks, guarantee_transaction_block=True, ) block = blocks[-1] unfinished = UnfinishedBlock( block.finished_sub_slots, block.reward_chain_block.get_unfinished(), block.challenge_chain_sp_proof, block.reward_chain_sp_proof, block.foliage, block.foliage_transaction_block, block.transactions_info, block.transactions_generator, [], ) pr = cProfile.Profile() pr.enable() start = time.time() res = await full_node_1.respond_unfinished_block( fnp.RespondUnfinishedBlock(unfinished), fake_peer) log.warning(f"Res: {res}") log.warning(f"Time for unfinished: {time.time() - start}") pr.create_stats() pr.dump_stats("./unfinished-benchmark.pstats") pr = cProfile.Profile() pr.enable() start = time.time() # No transactions generator, the full node already cached it from the unfinished block block_small = dataclasses.replace(block, transactions_generator=None) res = await full_node_1.full_node.respond_block( fnp.RespondBlock(block_small)) log.warning(f"Res: {res}") log.warning(f"Time for full block: {time.time() - start}") pr.create_stats() pr.dump_stats("./full-block-benchmark.pstats")
async def test_assert_announcement_consumed(self, two_nodes): num_blocks = 10 wallet_a = WALLET_A coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] receiver_puzzlehash = BURN_PUZZLE_HASH # Farm blocks blocks = bt.get_consecutive_blocks( num_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True ) full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes full_node_1 = full_node_api_1.full_node for block in blocks: await full_node_api_1.full_node.respond_block(full_node_protocol.RespondBlock(block)) # Coinbase that gets spent block1 = blocks[2] block2 = blocks[3] spend_coin_block_1 = None spend_coin_block_2 = None for coin in list(block1.get_included_reward_coins()): if coin.puzzle_hash == coinbase_puzzlehash: spend_coin_block_1 = coin for coin in list(block2.get_included_reward_coins()): if coin.puzzle_hash == coinbase_puzzlehash: spend_coin_block_2 = coin # This condition requires block2 coinbase to be spent block1_cvp = ConditionVarPair( ConditionOpcode.ASSERT_ANNOUNCEMENT, [Announcement(spend_coin_block_2.name(), bytes("test", "utf-8")).name()], ) block1_dic = {block1_cvp.opcode: [block1_cvp]} block1_spend_bundle = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, spend_coin_block_1, block1_dic ) # This condition requires block1 coinbase to be spent block2_cvp = ConditionVarPair( ConditionOpcode.CREATE_ANNOUNCEMENT, [bytes("test", "utf-8")], ) block2_dic = {block2_cvp.opcode: [block2_cvp]} block2_spend_bundle = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, spend_coin_block_2, block2_dic ) # Invalid block bundle assert block1_spend_bundle is not None # Create another block that includes our transaction invalid_new_blocks = bt.get_consecutive_blocks( 1, blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, transaction_data=block1_spend_bundle, guarantee_transaction_block=True, ) # Try to validate that block res, err, _ = await full_node_1.blockchain.receive_block(invalid_new_blocks[-1]) assert res == ReceiveBlockResult.INVALID_BLOCK assert err == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED # bundle_together contains both transactions bundle_together = SpendBundle.aggregate([block1_spend_bundle, block2_spend_bundle]) # Create another block that includes our transaction new_blocks = bt.get_consecutive_blocks( 1, blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, transaction_data=bundle_together, guarantee_transaction_block=True, ) # Try to validate newly created block res, err, _ = await full_node_1.blockchain.receive_block(new_blocks[-1]) assert res == ReceiveBlockResult.NEW_PEAK assert err is None
async def test_basic_coin_store(self, cache_size: uint32, db_version, softfork_height): wallet_a = WALLET_A reward_ph = wallet_a.get_new_puzzlehash() # Generate some coins blocks = bt.get_consecutive_blocks( 10, [], farmer_reward_puzzle_hash=reward_ph, pool_reward_puzzle_hash=reward_ph, ) coins_to_spend: List[Coin] = [] for block in blocks: if block.is_transaction_block(): for coin in block.get_included_reward_coins(): if coin.puzzle_hash == reward_ph: coins_to_spend.append(coin) spend_bundle = wallet_a.generate_signed_transaction( uint64(1000), wallet_a.get_new_puzzlehash(), coins_to_spend[0] ) async with DBConnection(db_version) as db_wrapper: coin_store = await CoinStore.create(db_wrapper, cache_size=cache_size) blocks = bt.get_consecutive_blocks( 10, blocks, farmer_reward_puzzle_hash=reward_ph, pool_reward_puzzle_hash=reward_ph, transaction_data=spend_bundle, ) # Adding blocks to the coin store should_be_included_prev: Set[Coin] = set() should_be_included: Set[Coin] = set() for block in blocks: farmer_coin, pool_coin = get_future_reward_coins(block) should_be_included.add(farmer_coin) should_be_included.add(pool_coin) if block.is_transaction_block(): if block.transactions_generator is not None: block_gen: BlockGenerator = BlockGenerator(block.transactions_generator, [], []) npc_result = get_name_puzzle_conditions( block_gen, bt.constants.MAX_BLOCK_COST_CLVM, cost_per_byte=bt.constants.COST_PER_BYTE, mempool_mode=False, height=softfork_height, ) tx_removals, tx_additions = tx_removals_and_additions(npc_result.npc_list) else: tx_removals, tx_additions = [], [] assert block.get_included_reward_coins() == should_be_included_prev if block.is_transaction_block(): assert block.foliage_transaction_block is not None await coin_store.new_block( block.height, block.foliage_transaction_block.timestamp, block.get_included_reward_coins(), tx_additions, tx_removals, ) if block.height != 0: with pytest.raises(Exception): await coin_store.new_block( block.height, block.foliage_transaction_block.timestamp, block.get_included_reward_coins(), tx_additions, tx_removals, ) for expected_coin in should_be_included_prev: # Check that the coinbase rewards are added record = await coin_store.get_coin_record(expected_coin.name()) assert record is not None assert not record.spent assert record.coin == expected_coin for coin_name in tx_removals: # Check that the removed coins are set to spent record = await coin_store.get_coin_record(coin_name) assert record.spent for coin in tx_additions: # Check that the added coins are added record = await coin_store.get_coin_record(coin.name()) assert not record.spent assert coin == record.coin should_be_included_prev = should_be_included.copy() should_be_included = set()
async def test_signage_points(self, two_nodes, empty_blockchain): test_rpc_port = uint16(21522) nodes, _ = two_nodes full_node_api_1, full_node_api_2 = nodes server_1 = full_node_api_1.full_node.server server_2 = full_node_api_2.full_node.server peer = await connect_and_get_peer(server_1, server_2) def stop_node_cb(): full_node_api_1._close() server_1.close_all() full_node_rpc_api = FullNodeRpcApi(full_node_api_1.full_node) config = bt.config hostname = config["self_hostname"] daemon_port = config["daemon_port"] rpc_cleanup = await start_rpc_server( full_node_rpc_api, hostname, daemon_port, test_rpc_port, stop_node_cb, bt.root_path, config, connect_to_daemon=False, ) try: client = await FullNodeRpcClient.create(self_hostname, test_rpc_port, bt.root_path, config) # Only provide one res = await client.get_recent_signage_point_or_eos(None, None) assert res is None res = await client.get_recent_signage_point_or_eos( std_hash(b"0"), std_hash(b"1")) assert res is None # Not found res = await client.get_recent_signage_point_or_eos( std_hash(b"0"), None) assert res is None res = await client.get_recent_signage_point_or_eos( None, std_hash(b"0")) assert res is None blocks = bt.get_consecutive_blocks(5) for block in blocks: await full_node_api_1.full_node.respond_block( full_node_protocol.RespondBlock(block)) blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=1, force_overflow=True) blockchain = full_node_api_1.full_node.blockchain second_blockchain = empty_blockchain for block in blocks: await _validate_and_add_block(second_blockchain, block) # Creates a signage point based on the last block peak_2 = second_blockchain.get_peak() sp: SignagePoint = get_signage_point( test_constants, blockchain, peak_2, peak_2.ip_sub_slot_total_iters(test_constants), uint8(4), [], peak_2.sub_slot_iters, ) # Don't have SP yet res = await client.get_recent_signage_point_or_eos( sp.cc_vdf.output.get_hash(), None) assert res is None # Add the last block await full_node_api_1.full_node.respond_block( full_node_protocol.RespondBlock(blocks[-1])) await full_node_api_1.respond_signage_point( full_node_protocol.RespondSignagePoint(uint8(4), sp.cc_vdf, sp.cc_proof, sp.rc_vdf, sp.rc_proof), peer) assert full_node_api_1.full_node.full_node_store.get_signage_point( sp.cc_vdf.output.get_hash()) is not None # Properly fetch a signage point res = await client.get_recent_signage_point_or_eos( sp.cc_vdf.output.get_hash(), None) assert res is not None assert "eos" not in res assert res["signage_point"] == sp assert not res["reverted"] blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=1) selected_eos = blocks[-1].finished_sub_slots[0] # Don't have EOS yet res = await client.get_recent_signage_point_or_eos( None, selected_eos.challenge_chain.get_hash()) assert res is None # Properly fetch an EOS for eos in blocks[-1].finished_sub_slots: await full_node_api_1.full_node.respond_end_of_sub_slot( full_node_protocol.RespondEndOfSubSlot(eos), peer) res = await client.get_recent_signage_point_or_eos( None, selected_eos.challenge_chain.get_hash()) assert res is not None assert "signage_point" not in res assert res["eos"] == selected_eos assert not res["reverted"] # Do another one but without sending the slot await full_node_api_1.full_node.respond_block( full_node_protocol.RespondBlock(blocks[-1])) blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=1) selected_eos = blocks[-1].finished_sub_slots[-1] await full_node_api_1.full_node.respond_block( full_node_protocol.RespondBlock(blocks[-1])) res = await client.get_recent_signage_point_or_eos( None, selected_eos.challenge_chain.get_hash()) assert res is not None assert "signage_point" not in res assert res["eos"] == selected_eos assert not res["reverted"] # Perform a reorg blocks = bt.get_consecutive_blocks(12, seed=b"1234") for block in blocks: await full_node_api_1.full_node.respond_block( full_node_protocol.RespondBlock(block)) # Signage point is no longer in the blockchain res = await client.get_recent_signage_point_or_eos( sp.cc_vdf.output.get_hash(), None) assert res["reverted"] assert res["signage_point"] == sp assert "eos" not in res # EOS is no longer in the blockchain res = await client.get_recent_signage_point_or_eos( None, selected_eos.challenge_chain.get_hash()) assert res is not None assert "signage_point" not in res assert res["eos"] == selected_eos assert res["reverted"] finally: # Checks that the RPC manages to stop the node client.close() await client.await_closed() await rpc_cleanup()
async def test_invalid_block(self, wallet_node_30_freeze): num_blocks = 5 full_nodes, wallets = wallet_node_30_freeze full_node_api: FullNodeSimulator = full_nodes[0] full_node_server = full_node_api.server 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(self_hostname, uint16(full_node_server._port)), None) for i in range(num_blocks): await full_node_api.farm_new_transaction_block( FarmNewBlockProtocol(ph)) funds = sum([ calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks) ]) # funds += calculate_base_farmer_reward(0) await asyncio.sleep(2) print(await wallet.get_confirmed_balance(), funds) await time_out_assert(10, wallet.get_confirmed_balance, funds) tx: TransactionRecord = await wallet.generate_signed_transaction( 100, ph, 0) current_blocks = await full_node_api.get_all_full_blocks() new_blocks = bt.get_consecutive_blocks( 1, block_list_input=current_blocks, transaction_data=tx.spend_bundle, guarantee_transaction_block=True) last_block = new_blocks[-1:][0] new_blocks_no_tx = bt.get_consecutive_blocks( 1, block_list_input=current_blocks, guarantee_transaction_block=True) last_block_no_tx = new_blocks_no_tx[-1:][0] result, error, fork = await full_node_api.full_node.blockchain.receive_block( last_block, None) assert error is not None assert error is Err.INITIAL_TRANSACTION_FREEZE assert result is ReceiveBlockResult.INVALID_BLOCK result, error, fork = await full_node_api.full_node.blockchain.receive_block( last_block_no_tx, None) assert error is None assert result is ReceiveBlockResult.NEW_PEAK after_freeze_blocks = bt.get_consecutive_blocks( 24, block_list_input=new_blocks_no_tx) for block in after_freeze_blocks: await full_node_api.full_node.blockchain.receive_block(block, None) assert full_node_api.full_node.blockchain.get_peak_height() == 30 new_blocks = bt.get_consecutive_blocks( 1, block_list_input=after_freeze_blocks, transaction_data=tx.spend_bundle, guarantee_transaction_block=True) last_block = new_blocks[-1:][0] result, error, fork = await full_node_api.full_node.blockchain.receive_block( last_block, None) assert error is None assert result is ReceiveBlockResult.NEW_PEAK
async def test1(self, two_nodes): num_blocks = 10 test_rpc_port = uint16(21522) full_node_1, full_node_2, server_1, server_2 = two_nodes blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10) for i in range(1, num_blocks): async for _ in full_node_1.respond_unfinished_block( full_node_protocol.RespondUnfinishedBlock(blocks[i]) ): pass async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(blocks[i]) ): pass def stop_node_cb(): full_node_1._close() server_1.close_all() rpc_cleanup = await start_full_node_rpc_server( full_node_1, stop_node_cb, test_rpc_port ) try: client = await FullNodeRpcClient.create(test_rpc_port) state = await client.get_blockchain_state() assert state["lca"].header_hash is not None assert not state["sync"]["sync_mode"] assert len(state["tips"]) > 0 assert state["difficulty"] > 0 assert state["ips"] > 0 assert state["min_iters"] > 0 block = await client.get_block(state["lca"].header_hash) assert block == blocks[7] assert (await client.get_block(bytes([1] * 32))) is None unf_block_headers = await client.get_unfinished_block_headers(5) assert len(unf_block_headers) == 1 assert unf_block_headers[0] == blocks[5].header header = await client.get_header(state["lca"].header_hash) assert header == blocks[7].header assert (await client.get_header_by_height(7)) == blocks[7].header assert (await client.get_header_by_height(100)) is None coins = await client.get_unspent_coins( blocks[-1].header.data.coinbase.puzzle_hash, blocks[-1].header_hash ) assert len(coins) == 16 coins_lca = await client.get_unspent_coins( blocks[-1].header.data.coinbase.puzzle_hash ) assert len(coins_lca) == 16 assert len(await client.get_connections()) == 0 await client.open_connection("localhost", server_2._port) await asyncio.sleep(2) connections = await client.get_connections() assert len(connections) == 1 await client.close_connection(connections[0]["node_id"]) assert len(await client.get_connections()) == 0 await asyncio.sleep(2) # Allow server to start except AssertionError: # 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()
async def test_basic_reorg(self): initial_block_count = 20 reorg_length = 15 blocks = bt.get_consecutive_blocks(test_constants, initial_block_count, [], 9) db_path = Path("blockchain_test.db") if db_path.exists(): db_path.unlink() connection = await aiosqlite.connect(db_path) coin_store = await CoinStore.create(connection) store = await BlockStore.create(connection) b: Blockchain = await Blockchain.create(coin_store, store, test_constants) try: for i in range(1, len(blocks)): await b.receive_block(blocks[i]) assert b.get_current_tips()[0].height == initial_block_count for c, block in enumerate(blocks): unspent = await coin_store.get_coin_record( block.get_coinbase().name(), block.header) unspent_fee = await coin_store.get_coin_record( block.get_fees_coin().name(), block.header) assert unspent.spent == 0 assert unspent_fee.spent == 0 assert unspent.confirmed_block_index == block.height assert unspent.spent_block_index == 0 assert unspent.name == block.get_coinbase().name() assert unspent_fee.name == block.get_fees_coin().name() blocks_reorg_chain = bt.get_consecutive_blocks( test_constants, reorg_length, blocks[:initial_block_count - 10], 9, b"1", ) for i in range(1, len(blocks_reorg_chain)): reorg_block = blocks_reorg_chain[i] result, removed, error_code = await b.receive_block(reorg_block ) if reorg_block.height < initial_block_count - 10: assert result == ReceiveBlockResult.ALREADY_HAVE_BLOCK elif reorg_block.height < initial_block_count - 1: assert result == ReceiveBlockResult.ADDED_AS_ORPHAN elif reorg_block.height >= initial_block_count: assert result == ReceiveBlockResult.ADDED_TO_HEAD unspent = await coin_store.get_coin_record( reorg_block.get_coinbase().name(), reorg_block.header) assert unspent.name == reorg_block.get_coinbase().name() assert unspent.confirmed_block_index == reorg_block.height assert unspent.spent == 0 assert unspent.spent_block_index == 0 assert error_code is None assert (b.get_current_tips()[0].height == initial_block_count - 10 + reorg_length - 1) except Exception as e: await connection.close() Path("blockchain_test.db").unlink() b.shut_down() raise e await connection.close() Path("blockchain_test.db").unlink() b.shut_down()
async def test_assert_fee_condition(self, two_nodes): num_blocks = 10 wallet_a = WALLET_A coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] receiver_puzzlehash = BURN_PUZZLE_HASH # Farm blocks blocks = bt.get_consecutive_blocks( num_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True) full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes full_node_1 = full_node_api_1.full_node for block in blocks: await full_node_api_1.full_node.respond_block( full_node_protocol.RespondBlock(block)) # Coinbase that gets spent block1 = blocks[2] spend_coin_block_1 = None for coin in list(block1.get_included_reward_coins()): if coin.puzzle_hash == coinbase_puzzlehash: spend_coin_block_1 = coin # This condition requires fee to be 10 mojo cvp_fee = ConditionWithArgs(ConditionOpcode.RESERVE_FEE, [int_to_bytes(10)]) # This spend bundle has 9 mojo as fee block1_dic_bad = {cvp_fee.opcode: [cvp_fee]} block1_dic_good = {cvp_fee.opcode: [cvp_fee]} block1_spend_bundle_bad = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, spend_coin_block_1, block1_dic_bad, fee=9) block1_spend_bundle_good = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, spend_coin_block_1, block1_dic_good, fee=10) log.warning(block1_spend_bundle_good.additions()) log.warning(f"Spend bundle fees: {block1_spend_bundle_good.fees()}") invalid_new_blocks = bt.get_consecutive_blocks( 1, blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, transaction_data=block1_spend_bundle_bad, guarantee_transaction_block=True, ) res, err, _ = await full_node_1.blockchain.receive_block( invalid_new_blocks[-1]) assert res == ReceiveBlockResult.INVALID_BLOCK assert err == Err.RESERVE_FEE_CONDITION_FAILED valid_new_blocks = bt.get_consecutive_blocks( 1, blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, transaction_data=block1_spend_bundle_good, guarantee_transaction_block=True, ) res, err, _ = await full_node_1.blockchain.receive_block( valid_new_blocks[-1]) assert err is None assert res == ReceiveBlockResult.NEW_PEAK
async def test_assert_block_age_exceeds(self, two_nodes): num_blocks = 10 wallet_a = WALLET_A coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] receiver_puzzlehash = BURN_PUZZLE_HASH # Farm blocks blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash) full_node_1, full_node_2, server_1, server_2 = two_nodes for block in blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block)): pass # Coinbase that gets spent block1 = blocks[1] # This condition requires block1 coinbase to be spent more than 10 block after it was farmed # block index has to be greater than (1 + 10 = 11) block1_cvp = ConditionVarPair(ConditionOpcode.ASSERT_BLOCK_AGE_EXCEEDS, int_to_bytes(10), None) block1_dic = {block1_cvp.opcode: [block1_cvp]} block1_spend_bundle = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, block1.get_coinbase(), block1_dic) # program that will be sent to early assert block1_spend_bundle is not None program = best_solution_program(block1_spend_bundle) aggsig = block1_spend_bundle.aggregated_signature # Create another block that includes our transaction dic_h = {11: (program, aggsig)} invalid_new_blocks = bt.get_consecutive_blocks(test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h) # Try to validate that block at index 11 next_block = invalid_new_blocks[11] error = await full_node_1.blockchain._validate_transactions( next_block, next_block.get_fees_coin().amount) assert error is Err.ASSERT_BLOCK_AGE_EXCEEDS_FAILED dic_h = {12: (program, aggsig)} valid_new_blocks = bt.get_consecutive_blocks(test_constants, 2, blocks[:11], 10, b"", coinbase_puzzlehash, dic_h) for block in valid_new_blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block)): pass # Try to validate that block at index 12 next_block = valid_new_blocks[12] error = await full_node_1.blockchain._validate_transactions( next_block, next_block.get_fees_coin().amount) assert error is None