def generate_unsigned_transaction( self, amount: uint64, new_puzzle_hash: bytes32, coin: Coin, condition_dic: Dict[ConditionOpcode, List[ConditionVarPair]], fee: int = 0, secret_key=None, ) -> List[CoinSolution]: spends = [] spend_value = coin.amount puzzle_hash = coin.puzzle_hash if secret_key is None: secret_key = self.get_private_key_for_puzzle_hash(puzzle_hash) pubkey = secret_key.get_g1() puzzle = puzzle_for_pk(bytes(pubkey)) if ConditionOpcode.CREATE_COIN not in condition_dic: condition_dic[ConditionOpcode.CREATE_COIN] = [] output = ConditionVarPair(ConditionOpcode.CREATE_COIN, [new_puzzle_hash, int_to_bytes(amount)]) condition_dic[output.opcode].append(output) amount_total = sum(int_from_bytes(cvp.vars[1]) for cvp in condition_dic[ConditionOpcode.CREATE_COIN]) change = spend_value - amount_total - fee if change > 0: change_puzzle_hash = self.get_new_puzzlehash() change_output = ConditionVarPair(ConditionOpcode.CREATE_COIN, [change_puzzle_hash, int_to_bytes(change)]) condition_dic[output.opcode].append(change_output) solution = self.make_solution(condition_dic) else: solution = self.make_solution(condition_dic) puzzle_solution_pair = Program.to([puzzle, solution]) spends.append(CoinSolution(coin, puzzle_solution_pair)) return spends
async def test_assert_fee_condition_wrong_fee(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_height = blocks[-1].height blocks = bt.get_consecutive_blocks( 3, block_list_input=blocks, guarantee_transaction_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_block(full_node_protocol.RespondBlock(block)) await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3) cvp = ConditionVarPair(ConditionOpcode.RESERVE_FEE, [int_to_bytes(10)]) dic = {cvp.opcode: [cvp]} spend_bundle1 = generate_test_spend_bundle(list(blocks[-1].get_included_reward_coins())[0], dic, uint64(9)) assert spend_bundle1 is not None tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle1) await full_node_1.respond_transaction(tx1, peer) mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name()) assert mempool_bundle is None
async def test_stealing_fee(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_height = blocks[-1].height blocks = bt.get_consecutive_blocks( 5, block_list_input=blocks, guarantee_transaction_block=True, farmer_reward_puzzle_hash=reward_ph, pool_reward_puzzle_hash=reward_ph, ) full_node_1, full_node_2, server_1, server_2 = two_nodes peer = await connect_and_get_peer(server_1, server_2) for block in blocks: await full_node_1.full_node.respond_block( full_node_protocol.RespondBlock(block)) await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 5) receiver_puzzlehash = BURN_PUZZLE_HASH cvp = ConditionVarPair(ConditionOpcode.RESERVE_FEE, [int_to_bytes(10)]) dic = {cvp.opcode: [cvp]} fee = 9 coin_1 = list(blocks[-2].get_included_reward_coins())[0] coin_2 = None for coin in list(blocks[-1].get_included_reward_coins()): if coin.amount == coin_1.amount: coin_2 = coin spend_bundle1 = generate_test_spend_bundle(coin_1, dic, uint64(fee)) steal_fee_spendbundle = WALLET_A.generate_signed_transaction( coin_1.amount + fee - 4, receiver_puzzlehash, coin_2) 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) await full_node_1.respond_transaction(tx1, peer) mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle( spend_bundle1.name()) assert mempool_bundle is None
def generate_unsigned_transaction( self, amount, newpuzzlehash, coin: Coin, condition_dic: Dict[ConditionOpcode, List[ConditionVarPair]], fee: int = 0, secretkey=None, ): spends = [] spend_value = coin.amount puzzle_hash = coin.puzzle_hash if secretkey is None: pubkey, secretkey = self.get_keys(puzzle_hash) else: pubkey = secretkey.get_g1() puzzle = puzzle_for_pk(bytes(pubkey)) if ConditionOpcode.CREATE_COIN not in condition_dic: condition_dic[ConditionOpcode.CREATE_COIN] = [] output = ConditionVarPair( ConditionOpcode.CREATE_COIN, newpuzzlehash, int_to_bytes(amount) ) condition_dic[output.opcode].append(output) amount_total = sum( int_from_bytes(cvp.var2) for cvp in condition_dic[ConditionOpcode.CREATE_COIN] ) change = spend_value - amount_total - fee if change > 0: changepuzzlehash = self.get_new_puzzlehash() change_output = ConditionVarPair( ConditionOpcode.CREATE_COIN, changepuzzlehash, int_to_bytes(change) ) condition_dic[output.opcode].append(change_output) solution = self.make_solution(condition_dic) else: solution = self.make_solution(condition_dic) spends.append((puzzle, CoinSolution(coin, solution))) return spends
async def test_assert_fee_condition_wrong_fee(self, two_nodes): num_blocks = 2 wallet_a = bt.get_pool_wallet_tool() wallet_receiver = WalletTool() receiver_puzzlehash = wallet_receiver.get_new_puzzlehash() 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] 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]} spend_bundle1 = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, block.get_coinbase(), dic, 9) assert spend_bundle1 is not None 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
def __bytes__(self): return self.parent_coin_info + self.puzzle_hash + int_to_bytes(self.amount)
def __bytes__(self): f = io.BytesIO() f.write(self.parent_coin_info) f.write(self.puzzle_hash) f.write(int_to_bytes(self.amount)) return f.getvalue()
async def test_assert_time_exceeds(self, two_nodes): num_blocks = 10 wallet_a = WalletTool() coinbase_puzzlehash = wallet_a.get_new_puzzlehash() wallet_receiver = WalletTool() receiver_puzzlehash = wallet_receiver.get_new_puzzlehash() # 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 test_assert_block_age_exceeds(self, two_nodes): num_blocks = 10 wallet_a = WalletTool() coinbase_puzzlehash = wallet_a.get_new_puzzlehash() wallet_receiver = WalletTool() receiver_puzzlehash = wallet_receiver.get_new_puzzlehash() # 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
async def test_assert_fee_condition(self, two_nodes): num_blocks = 10 wallet_a = WalletTool() coinbase_puzzlehash = wallet_a.get_new_puzzlehash() wallet_receiver = WalletTool() receiver_puzzlehash = wallet_receiver.get_new_puzzlehash() # 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 fee to be 10 mojo cvp_fee = ConditionVarPair(ConditionOpcode.ASSERT_FEE, int_to_bytes(10), None) block1_dic = {cvp_fee.opcode: [cvp_fee]} # This spendbundle has 9 mojo as fee invalid_spend_bundle = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, block1.get_coinbase(), block1_dic, 9) assert invalid_spend_bundle is not None program = best_solution_program(invalid_spend_bundle) aggsig = invalid_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, fees=uint64(9), ) # 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_FEE_CONDITION_FAILED # This condition requires fee to be 10 mojo cvp_fee = ConditionVarPair(ConditionOpcode.ASSERT_FEE, int_to_bytes(10), None) condition_dict = {cvp_fee.opcode: [cvp_fee]} valid_spend_bundle = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, block1.get_coinbase(), condition_dict, 10) assert valid_spend_bundle is not None valid_program = best_solution_program(valid_spend_bundle) aggsig = valid_spend_bundle.aggregated_signature dic_h = {11: (valid_program, aggsig)} valid_new_blocks = bt.get_consecutive_blocks( test_constants, 1, blocks[:11], 10, b"", coinbase_puzzlehash, dic_h, fees=uint64(10), ) next_block = valid_new_blocks[11] fee_base = calculate_base_fee(next_block.height) error = await full_node_1.blockchain._validate_transactions( next_block, fee_base) assert error is None for block in valid_new_blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block)): pass
async def test_stealing_fee(self, two_nodes): num_blocks = 2 wallet_a = bt.get_pool_wallet_tool() wallet_receiver = bt.get_farmer_wallet_tool() receiver_puzzlehash = wallet_receiver.get_new_puzzlehash() 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 = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, 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_new_transaction(self, two_nodes, wallet_blocks_five): full_node_1, full_node_2, server_1, server_2 = two_nodes wallet_a, wallet_receiver, blocks = wallet_blocks_five conditions_dict: Dict = {ConditionOpcode.CREATE_COIN: []} # Mempool has capacity of 100, make 110 unspents that we can use puzzle_hashes = [] for _ in range(110): receiver_puzzlehash = wallet_receiver.get_new_puzzlehash() puzzle_hashes.append(receiver_puzzlehash) output = ConditionVarPair( ConditionOpcode.CREATE_COIN, receiver_puzzlehash, int_to_bytes(1000) ) conditions_dict[ConditionOpcode.CREATE_COIN].append(output) spend_bundle = wallet_a.generate_signed_transaction( 100, receiver_puzzlehash, blocks[1].get_coinbase(), condition_dic=conditions_dict, ) assert spend_bundle is not None new_transaction = fnp.NewTransaction( spend_bundle.get_hash(), uint64(100), uint64(100) ) # Not seen msgs = [x async for x in full_node_1.new_transaction(new_transaction)] assert len(msgs) == 1 assert msgs[0].message.data == fnp.RequestTransaction(spend_bundle.get_hash()) respond_transaction_2 = fnp.RespondTransaction(spend_bundle) [x async for x in full_node_1.respond_transaction(respond_transaction_2)] program = best_solution_program(spend_bundle) aggsig = spend_bundle.aggregated_signature dic_h = {5: (program, aggsig)} blocks_new = bt.get_consecutive_blocks( test_constants, 3, blocks[:-1], 10, transaction_data_at_height=dic_h, ) # Already seen msgs = [x async for x in full_node_1.new_transaction(new_transaction)] assert len(msgs) == 0 # Farm one block for block in blocks_new: [_ async for _ in full_node_1.respond_block(fnp.RespondBlock(block))] spend_bundles = [] total_fee = 0 # Fill mempool for puzzle_hash in puzzle_hashes: coin_record = ( await full_node_1.coin_store.get_coin_records_by_puzzle_hash( puzzle_hash, blocks_new[-3].header ) )[0] receiver_puzzlehash = wallet_receiver.get_new_puzzlehash() fee = random.randint(2, 499) spend_bundle = wallet_receiver.generate_signed_transaction( 500, receiver_puzzlehash, coin_record.coin, fee=fee ) respond_transaction = fnp.RespondTransaction(spend_bundle) res = [ x async for x in full_node_1.respond_transaction(respond_transaction) ] # Added to mempool if len(res) > 0: total_fee += fee spend_bundles.append(spend_bundle) # Mempool is full new_transaction = fnp.NewTransaction( token_bytes(32), uint64(1000000), uint64(1) ) msgs = [x async for x in full_node_1.new_transaction(new_transaction)] assert len(msgs) == 0 agg = SpendBundle.aggregate(spend_bundles) program = best_solution_program(agg) aggsig = agg.aggregated_signature dic_h = {8: (program, aggsig)} blocks_new = bt.get_consecutive_blocks( test_constants, 1, blocks_new, 10, transaction_data_at_height=dic_h, fees=uint64(total_fee), ) # Farm one block to clear mempool [_ async for _ in full_node_1.respond_block(fnp.RespondBlock(blocks_new[-1]))]