async def test_double_spend_same_bundle(self, two_nodes): num_blocks = 2 blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10, b"") full_node_1, full_node_2, server_1, server_2 = two_nodes block = blocks[1] async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block) ): pass spend_bundle1 = generate_test_spend_bundle(block.get_coinbase()) assert spend_bundle1 is not None spend_bundle2 = generate_test_spend_bundle( block.get_coinbase(), newpuzzlehash=BURN_PUZZLE_HASH_2, ) assert spend_bundle2 is not None spend_bundle_combined = SpendBundle.aggregate([spend_bundle1, spend_bundle2]) tx: full_node_protocol.RespondTransaction = ( full_node_protocol.RespondTransaction(spend_bundle_combined) ) messages = [] async for outbound in full_node_1.respond_transaction(tx): messages.append(outbound) sb = full_node_1.mempool_manager.get_spendbundle(spend_bundle_combined.name()) assert sb is None
async def test_validate_blockchain_duplicate_output(self, two_nodes): num_blocks = 3 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 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)) 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) spend_bundle_double = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, spend_coin) block_spendbundle = SpendBundle.aggregate( [spend_bundle, spend_bundle_double]) new_blocks = bt.get_consecutive_blocks( 1, block_list_input=blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, transaction_data=block_spendbundle, guarantee_transaction_block=True, ) next_block = new_blocks[-1] res, err, _ = await full_node_1.blockchain.receive_block(next_block) assert res == ReceiveBlockResult.INVALID_BLOCK assert err == Err.DUPLICATE_OUTPUT
async def test_validate_blockchain_with_double_spend(self, two_nodes): num_blocks = 5 wallet_a = WalletTool() coinbase_puzzlehash = wallet_a.get_new_puzzlehash() wallet_receiver = WalletTool() receiver_puzzlehash = wallet_receiver.get_new_puzzlehash() 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() ) spend_bundle_double = wallet_a.generate_signed_transaction( 1001, receiver_puzzlehash, spent_block.get_coinbase() ) block_spendbundle = SpendBundle.aggregate([spend_bundle, spend_bundle_double]) program = best_solution_program(block_spendbundle) aggsig = block_spendbundle.aggregated_signature dic_h = {(num_blocks + 1): (program, aggsig)} new_blocks = bt.get_consecutive_blocks( test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h ) next_block = new_blocks[num_blocks + 1] error = await full_node_1.blockchain._validate_transactions( next_block, next_block.get_fees_coin().amount ) assert error is Err.DOUBLE_SPEND
async def test_correct_coin_consumed(self, two_nodes): num_blocks = 2 blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10, b"") full_node_1, full_node_2, server_1, server_2 = two_nodes block = blocks[1] block2 = blocks[2] for b in blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(b) ): pass cvp = ConditionVarPair( ConditionOpcode.ASSERT_COIN_CONSUMED, block2.get_coinbase().name(), None, ) dic = {cvp.opcode: [cvp]} spend_bundle1 = generate_test_spend_bundle(block.get_coinbase(), dic) spend_bundle2 = generate_test_spend_bundle(block2.get_coinbase()) bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2]) tx1: full_node_protocol.RespondTransaction = ( full_node_protocol.RespondTransaction(bundle) ) async for _ in full_node_1.respond_transaction(tx1): outbound: OutboundMessage = _ # Maybe transaction means that it's accepted in mempool assert outbound.message.function == "new_transaction" mempool_bundle = full_node_1.mempool_manager.get_spendbundle(bundle.name()) assert mempool_bundle is bundle
async def generate_new_coloured_coin(self, amount: uint64) -> Optional[SpendBundle]: coins = await self.standard_wallet.select_coins(amount) if coins is None: return None origin = coins.copy().pop() origin_id = origin.name() # self.add_parent(origin_id, origin_id) cc_core = cc_wallet_puzzles.cc_make_core(origin_id) parent_info = {} parent_info[origin_id] = ( origin.parent_coin_info, origin.puzzle_hash, origin.amount, ) cc_info: CCInfo = CCInfo(cc_core, [], origin_id.hex()) await self.save_info(cc_info) cc_inner = await self.get_new_inner_hash() cc_puzzle = cc_wallet_puzzles.cc_make_puzzle(cc_inner, cc_core) cc_puzzle_hash = cc_puzzle.get_tree_hash() tx_record: Optional[ TransactionRecord ] = await self.standard_wallet.generate_signed_transaction( amount, cc_puzzle_hash, uint64(0), origin_id, coins ) self.log.warning(f"cc_puzzle_hash is {cc_puzzle_hash}") eve_coin = Coin(origin_id, cc_puzzle_hash, amount) if tx_record is None or tx_record.spend_bundle is None: return None eve_spend = cc_generate_eve_spend(eve_coin, cc_puzzle) full_spend = SpendBundle.aggregate([tx_record.spend_bundle, eve_spend]) return full_spend
async def test_validate_blockchain_duplicate_output(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()) spend_bundle_double = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, spent_block.get_coinbase()) block_spendbundle = SpendBundle.aggregate( [spend_bundle, spend_bundle_double]) program = best_solution_program(block_spendbundle) aggsig = block_spendbundle.aggregated_signature dic_h = {(num_blocks + 1): (program, aggsig)} new_blocks = bt.get_consecutive_blocks(test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h) next_block = new_blocks[(num_blocks + 1)] error = await full_node_1.blockchain._validate_transactions( next_block, next_block.get_fees_coin().amount) assert error is Err.DUPLICATE_OUTPUT
async def test_correct_coin_consumed(self, two_nodes): reward_ph = WALLET_A.get_new_puzzlehash() full_node_1, full_node_2, server_1, server_2 = two_nodes blocks = await full_node_1.get_all_full_blocks() start_sub_height = blocks[-1].sub_block_height blocks = bt.get_consecutive_blocks( 4, block_list_input=blocks, guarantee_block=True, farmer_reward_puzzle_hash=reward_ph, pool_reward_puzzle_hash=reward_ph, ) peer = await connect_and_get_peer(server_1, server_2) for block in blocks: await full_node_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) await time_out_assert(60, node_height_at_least, True, full_node_1, start_sub_height + 4) coin_1 = list(blocks[-2].get_included_reward_coins())[0] coin_2 = list(blocks[-1].get_included_reward_coins())[0] cvp = ConditionVarPair(ConditionOpcode.ASSERT_COIN_CONSUMED, [coin_2.name()]) dic = {cvp.opcode: [cvp]} spend_bundle1 = generate_test_spend_bundle(coin_1, dic) spend_bundle2 = generate_test_spend_bundle(coin_2) bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2]) tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(bundle) await full_node_1.respond_transaction(tx1, peer) mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name()) assert mempool_bundle is bundle
async def create_offer_for_ids( self, offer: Dict[int, int] ) -> Tuple[bool, Optional[SpendBundle], Optional[str]]: """ Offer is dictionary of wallet ids and amount """ spend_bundle = None try: for id in offer.keys(): amount = offer[id] wallet_id = uint32(int(id)) wallet = self.wallet_state_manager.wallets[wallet_id] if isinstance(wallet, CCWallet): balance = await wallet.get_confirmed_balance() if balance < abs(amount) and amount < 0: raise Exception( f"insufficient funds in wallet {wallet_id}") if balance == 0 and amount > 0: if spend_bundle is None: to_exclude: List[Coin] = [] else: to_exclude = spend_bundle.removals() zero_spend_bundle: Optional[ SpendBundle] = await wallet.generate_zero_val_coin( False, to_exclude) if zero_spend_bundle is None: raise Exception( "Failed to generate offer. Zero value coin not created." ) if spend_bundle is None: spend_bundle = zero_spend_bundle else: spend_bundle = SpendBundle.aggregate( [spend_bundle, zero_spend_bundle]) additions = zero_spend_bundle.additions() removals = zero_spend_bundle.removals() zero_val_coin: Optional[Coin] = None for add in additions: if add not in removals and add.amount == 0: zero_val_coin = add new_spend_bundle = await wallet.create_spend_bundle_relative_amount( amount, zero_val_coin) else: new_spend_bundle = await wallet.create_spend_bundle_relative_amount( amount) elif isinstance(wallet, Wallet): if spend_bundle is None: to_exclude = [] else: to_exclude = spend_bundle.removals() new_spend_bundle = await wallet.create_spend_bundle_relative_chia( amount, to_exclude) else: return False, None, "unssuported wallet type" if new_spend_bundle.removals( ) == [] or new_spend_bundle is None: raise Exception(f"Wallet {id} was unable to create offer.") if spend_bundle is None: spend_bundle = new_spend_bundle else: spend_bundle = SpendBundle.aggregate( [spend_bundle, new_spend_bundle]) return True, spend_bundle, None except Exception as e: return False, None, str(e)
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_request_removals(self, two_nodes, wallet_blocks): full_node_1, full_node_2, server_1, server_2 = two_nodes wallet_a, wallet_receiver, blocks = wallet_blocks await server_2.start_client(PeerInfo("localhost", uint16(server_1._port)), None) blocks_list = await get_block_path(full_node_1) blocks_new = bt.get_consecutive_blocks( test_constants, 5, seed=b"test_request_removals" ) # Request removals for nonexisting block fails msgs = [ _ async for _ in full_node_1.request_removals( wallet_protocol.RequestRemovals( blocks_new[-1].height, blocks_new[-1].header_hash, None ) ) ] assert len(msgs) == 1 assert isinstance(msgs[0].message.data, wallet_protocol.RejectRemovalsRequest) # Request removals for orphaned block fails for block in blocks_new: async for _ in full_node_1.respond_block(fnp.RespondBlock(block)): pass msgs = [ _ async for _ in full_node_1.request_removals( wallet_protocol.RequestRemovals( blocks_new[-1].height, blocks_new[-1].header_hash, None ) ) ] assert len(msgs) == 1 assert isinstance(msgs[0].message.data, wallet_protocol.RejectRemovalsRequest) # If there are no transactions, empty proof and coins blocks_new = bt.get_consecutive_blocks( test_constants, 10, block_list=blocks_list, ) for block in blocks_new: [_ async for _ in full_node_1.respond_block(fnp.RespondBlock(block))] msgs = [ _ async for _ in full_node_1.request_removals( wallet_protocol.RequestRemovals( blocks_new[-4].height, blocks_new[-4].header_hash, None ) ) ] assert len(msgs) == 1 assert isinstance(msgs[0].message.data, wallet_protocol.RespondRemovals) assert len(msgs[0].message.data.coins) == 0 assert msgs[0].message.data.proofs is None # Add a block with transactions spend_bundles = [] for i in range(5): spend_bundles.append( wallet_a.generate_signed_transaction( 100, wallet_a.get_new_puzzlehash(), blocks_new[i - 8].get_coinbase(), ) ) height_with_transactions = len(blocks_new) + 1 agg = SpendBundle.aggregate(spend_bundles) dic_h = { height_with_transactions: ( best_solution_program(agg), agg.aggregated_signature, ) } blocks_new = bt.get_consecutive_blocks( test_constants, 5, block_list=blocks_new, transaction_data_at_height=dic_h ) for block in blocks_new: [_ async for _ in full_node_1.respond_block(fnp.RespondBlock(block))] # If no coins requested, respond all coins and NO proof msgs = [ _ async for _ in full_node_1.request_removals( wallet_protocol.RequestRemovals( blocks_new[height_with_transactions].height, blocks_new[height_with_transactions].header_hash, None, ) ) ] assert len(msgs) == 1 assert isinstance(msgs[0].message.data, wallet_protocol.RespondRemovals) assert len(msgs[0].message.data.coins) == 5 assert msgs[0].message.data.proofs is None removals_merkle_set = MerkleSet() for sb in spend_bundles: for coin in sb.removals(): if coin is not None: removals_merkle_set.add_already_hashed(coin.name()) # Ask for one coin and check PoI coin_list = [spend_bundles[0].removals()[0].name()] msgs = [ _ async for _ in full_node_1.request_removals( wallet_protocol.RequestRemovals( blocks_new[height_with_transactions].height, blocks_new[height_with_transactions].header_hash, coin_list, ) ) ] assert len(msgs) == 1 assert isinstance(msgs[0].message.data, wallet_protocol.RespondRemovals) assert len(msgs[0].message.data.coins) == 1 assert msgs[0].message.data.proofs is not None assert len(msgs[0].message.data.proofs) == 1 assert confirm_included_already_hashed( blocks_new[height_with_transactions].header.data.removals_root, coin_list[0], msgs[0].message.data.proofs[0][1], ) # Ask for one coin and check PoE coin_list = [token_bytes(32)] msgs = [ _ async for _ in full_node_1.request_removals( wallet_protocol.RequestRemovals( blocks_new[height_with_transactions].height, blocks_new[height_with_transactions].header_hash, coin_list, ) ) ] assert len(msgs) == 1 assert isinstance(msgs[0].message.data, wallet_protocol.RespondRemovals) assert len(msgs[0].message.data.coins) == 1 assert msgs[0].message.data.coins[0][1] is None assert msgs[0].message.data.proofs is not None assert len(msgs[0].message.data.proofs) == 1 assert confirm_not_included_already_hashed( blocks_new[height_with_transactions].header.data.removals_root, coin_list[0], msgs[0].message.data.proofs[0][1], ) # Ask for two coins coin_list = [spend_bundles[0].removals()[0].name(), token_bytes(32)] msgs = [ _ async for _ in full_node_1.request_removals( wallet_protocol.RequestRemovals( blocks_new[height_with_transactions].height, blocks_new[height_with_transactions].header_hash, coin_list, ) ) ] assert len(msgs) == 1 assert isinstance(msgs[0].message.data, wallet_protocol.RespondRemovals) assert len(msgs[0].message.data.coins) == 2 assert msgs[0].message.data.coins[0][1] is not None assert msgs[0].message.data.coins[1][1] is None assert msgs[0].message.data.proofs is not None assert len(msgs[0].message.data.proofs) == 2 assert confirm_included_already_hashed( blocks_new[height_with_transactions].header.data.removals_root, coin_list[0], msgs[0].message.data.proofs[0][1], ) assert confirm_not_included_already_hashed( blocks_new[height_with_transactions].header.data.removals_root, coin_list[1], msgs[0].message.data.proofs[1][1], )