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_invalid_announcement_consumed_two(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 if len(blocks) > 0 else -1 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) coin_1 = list(blocks[-2].get_included_reward_coins())[0] coin_2 = list(blocks[-1].get_included_reward_coins())[0] announce = Announcement(coin_1.name(), bytes("test", "utf-8")) cvp = ConditionVarPair(ConditionOpcode.ASSERT_ANNOUNCEMENT, [announce.name()]) dic = {cvp.opcode: [cvp]} cvp2 = ConditionVarPair( ConditionOpcode.CREATE_ANNOUNCEMENT, [bytes("test", "utf-8")], ) dic2 = {cvp.opcode: [cvp2]} spend_bundle1 = generate_test_spend_bundle(coin_1, dic) spend_bundle2 = generate_test_spend_bundle(coin_2, dic2) bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2]) 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( bundle.name()) assert mempool_bundle is None
async def test_assert_time_exceeds(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] for b in blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(b) ): pass time_now = uint64(int(time() * 1000)) cvp = ConditionVarPair( ConditionOpcode.ASSERT_TIME_EXCEEDS, time_now.to_bytes(8, "big"), None ) dic = {cvp.opcode: [cvp]} spend_bundle1 = generate_test_spend_bundle(block.get_coinbase(), dic) assert spend_bundle1 is not None tx1: full_node_protocol.RespondTransaction = ( full_node_protocol.RespondTransaction(spend_bundle1) ) 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" sb1 = full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name()) assert sb1 is spend_bundle1
async def test_correct_block_index(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 cvp = ConditionVarPair( ConditionOpcode.ASSERT_BLOCK_INDEX_EXCEEDS, uint64(1).to_bytes(4, "big"), None, ) dic = {ConditionOpcode.ASSERT_BLOCK_INDEX_EXCEEDS: [cvp]} spend_bundle1 = generate_test_spend_bundle(block.get_coinbase(), dic) assert spend_bundle1 is not None tx1: full_node_protocol.RespondTransaction = ( full_node_protocol.RespondTransaction(spend_bundle1) ) 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" sb1 = full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name()) assert sb1 is spend_bundle1
async def test_invalid_my_id(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] for b in blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(b) ): pass cvp = ConditionVarPair( ConditionOpcode.ASSERT_MY_COIN_ID, blocks[2].get_coinbase().name(), None, ) dic = {cvp.opcode: [cvp]} spend_bundle1 = generate_test_spend_bundle(block.get_coinbase(), dic) assert spend_bundle1 is not None tx1: full_node_protocol.RespondTransaction = ( full_node_protocol.RespondTransaction(spend_bundle1) ) 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" sb1 = full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name()) assert sb1 is None
async def test_correct_my_id(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_MY_COIN_ID, block.get_coinbase().name(), None) dic = {cvp.opcode: [cvp]} spend_bundle1 = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, block.get_coinbase(), dic) assert spend_bundle1 is not None tx1: full_node_protocol.RespondTransaction = ( full_node_protocol.RespondTransaction(spend_bundle1)) 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" sb1 = full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name()) assert sb1 is spend_bundle1
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_correct_block_age(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( 4, 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 + 4) cvp = ConditionVarPair(ConditionOpcode.ASSERT_HEIGHT_AGE_EXCEEDS, [uint64(1).to_bytes(4, "big")]) dic = {cvp.opcode: [cvp]} spend_bundle1 = generate_test_spend_bundle(list(blocks[-2].get_included_reward_coins())[0], dic) 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) sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name()) assert sb1 is spend_bundle1
async def test_invalid_my_amount(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 if len(blocks) > 0 else -1 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) coin = list(blocks[-1].get_included_reward_coins())[0] cvp = ConditionVarPair(ConditionOpcode.ASSERT_MY_AMOUNT, [1000]) dic = {cvp.opcode: [cvp]} spend_bundle1 = generate_test_spend_bundle(coin, dic) 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) sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name()) assert sb1 is None
async def test_invalid_block_age(self, two_nodes): num_blocks = 2 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 block = blocks[1] async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block)): pass cvp = ConditionVarPair(ConditionOpcode.ASSERT_BLOCK_AGE_EXCEEDS, uint64(5).to_bytes(4, "big"), None) dic = {cvp.opcode: [cvp]} spend_bundle1 = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, block.header.data.coinbase, dic) assert spend_bundle1 is not None tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction( spend_bundle1) 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" sb1 = full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name()) assert sb1 is None
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 = ConditionVarPair(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_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
def get_name_puzzle_conditions(block_program: SerializedProgram, safe_mode: bool): # TODO: allow generator mod to take something (future) # TODO: write more tests block_program_args = SerializedProgram.from_bytes(b"\x80") try: if safe_mode: cost, result = GENERATOR_MOD.run_safe_with_cost(block_program, block_program_args) else: cost, result = GENERATOR_MOD.run_with_cost(block_program, block_program_args) npc_list = [] opcodes = set(item.value for item in ConditionOpcode) for res in result.as_iter(): conditions_list = [] name = std_hash( bytes( res.first().first().as_atom() + res.first().rest().first().as_atom() + res.first().rest().rest().first().as_atom() ) ) puzzle_hash = bytes32(res.first().rest().first().as_atom()) for cond in res.rest().first().as_iter(): if cond.first().as_atom() in opcodes: opcode = ConditionOpcode(cond.first().as_atom()) elif not safe_mode: opcode = ConditionOpcode.UNKNOWN else: return "Unknown operator in safe mode.", None, None if len(list(cond.as_iter())) > 1: cond_var_list = [] for cond_1 in cond.rest().as_iter(): cond_var_list.append(cond_1.as_atom()) cvl = ConditionVarPair(opcode, cond_var_list) else: cvl = ConditionVarPair(opcode, []) conditions_list.append(cvl) conditions_dict = conditions_by_opcode(conditions_list) if conditions_dict is None: conditions_dict = {} npc_list.append(NPC(name, puzzle_hash, [(a, b) for a, b in conditions_dict.items()])) return None, npc_list, uint64(cost) except Exception: tb = traceback.format_exc() return tb, None, 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 parse_sexp_to_condition( sexp: Program, ) -> Tuple[Optional[Err], Optional[ConditionVarPair]]: """ Takes a ChiaLisp sexp and returns a ConditionVarPair. If it fails, returns an Error """ if not sexp.listp(): return Err.SEXP_ERROR, None items = sexp.as_python() if not isinstance(items[0], bytes): return Err.INVALID_CONDITION, None try: opcode = ConditionOpcode(items[0]) except ValueError: opcode = ConditionOpcode.UNKNOWN if len(items) == 3: return None, ConditionVarPair(opcode, [items[1], items[2]]) return None, ConditionVarPair(opcode, [items[1]])
async def test_assert_time_exceeds_both_cases(self, two_nodes): num_blocks = 2 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 block = blocks[1] for b in blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(b) ): pass time_now = uint64(int(time() * 1000)) time_now_plus_3 = time_now + 3000 cvp = ConditionVarPair( ConditionOpcode.ASSERT_TIME_EXCEEDS, time_now_plus_3.to_bytes(8, "big"), None, ) dic = {cvp.opcode: [cvp]} spend_bundle1 = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, block.header.data.coinbase, dic ) assert spend_bundle1 is not None tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction( spend_bundle1 ) async for _ in full_node_1.respond_transaction(tx1): outbound: OutboundMessage = _ assert outbound.message.function != "new_transaction" # Sleep so that 3 sec passes await asyncio.sleep(3) tx2: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction( spend_bundle1 ) async for _ in full_node_1.respond_transaction(tx2): outbound_2: OutboundMessage = _ # Maybe transaction means that it's accepted in mempool assert outbound_2.message.function == "new_transaction" sb1 = full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name()) assert sb1 is spend_bundle1
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 = 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 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.header.data.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 == False mempool_bundle = full_node_1.mempool_manager.get_spendbundle( spend_bundle1.name() ) assert mempool_bundle is None
async def test_correct_coin_consumed(self, two_nodes): num_blocks = 2 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 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.header.data.coinbase.name(), None, ) dic = {cvp.opcode: [cvp]} spend_bundle1 = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, block.header.data.coinbase, dic ) spend_bundle2 = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, block2.header.data.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 test_assert_time_exceeds_both_cases(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 if len(blocks) > 0 else -1 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) time_now = uint64(int(time() * 1000)) time_now_plus_3 = time_now + 3000 cvp = ConditionVarPair(ConditionOpcode.ASSERT_SECONDS_NOW_EXCEEDS, [time_now_plus_3.to_bytes(8, "big")]) dic = {cvp.opcode: [cvp]} spend_bundle1 = generate_test_spend_bundle( list(blocks[-1].get_included_reward_coins())[0], dic) 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) # Sleep so that 3 sec passes await asyncio.sleep(3) tx2: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction( spend_bundle1) await full_node_1.respond_transaction(tx2, peer) sb1 = full_node_1.full_node.mempool_manager.get_spendbundle( spend_bundle1.name()) assert sb1 is spend_bundle1
async def test_assert_fee_condition(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] 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 = generate_test_spend_bundle(block.get_coinbase(), dic, 10) 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 mempool_bundle = full_node_1.mempool_manager.get_spendbundle( spend_bundle1.name() ) assert mempool_bundle is not None
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 test_invalid_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 + 3) for b in blocks: await full_node_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(b)) 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) 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_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
AggSig has assigned cost of 20vBytes, simple CLVM program is benchmarked against it. """ wallet_tool = WalletTool() benchmark_all_operators() secret_key: PrivateKey = PrivateKey.from_seed(bytes([2] * 32)) puzzles = [] solutions = [] private_keys = [] public_keys = [] for i in range(0, 1000): private_key: PrivateKey = master_sk_to_wallet_sk(secret_key, uint32(i)) public_key = private_key.public_key() solution = wallet_tool.make_solution({ ConditionOpcode.ASSERT_MY_COIN_ID: [ ConditionVarPair(ConditionOpcode.ASSERT_MY_COIN_ID, token_bytes(), None) ] }) puzzle = puzzle_for_pk(bytes(public_key)) puzzles.append(puzzle) solutions.append(solution) private_keys.append(private_key) public_keys.append(public_key) # Run Puzzle 1000 times puzzle_start = time.time() clvm_cost = 0 for i in range(0, 1000): cost_run, sexp = run_program(puzzles[i], solutions[i]) clvm_cost += cost_run
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_assert_my_coin_id(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 spent_block = blocks[1] bad_block = blocks[2] valid_cvp = ConditionVarPair( ConditionOpcode.ASSERT_MY_COIN_ID, spent_block.get_coinbase().name(), None, ) valid_dic = {valid_cvp.opcode: [valid_cvp]} bad_cvp = ConditionVarPair( ConditionOpcode.ASSERT_MY_COIN_ID, bad_block.get_coinbase().name(), None, ) bad_dic = {bad_cvp.opcode: [bad_cvp]} bad_spend_bundle = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, spent_block.get_coinbase(), bad_dic) valid_spend_bundle = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, spent_block.get_coinbase(), valid_dic) # Invalid block bundle assert bad_spend_bundle is not None invalid_program = best_solution_program(bad_spend_bundle) aggsig = bad_spend_bundle.aggregated_signature # Create another block that includes our transaction dic_h = {11: (invalid_program, aggsig)} invalid_new_blocks = bt.get_consecutive_blocks(test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h) # Try to validate that block 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_MY_COIN_ID_FAILED # Valid block bundle assert valid_spend_bundle is not None valid_program = best_solution_program(valid_spend_bundle) aggsig = valid_spend_bundle.aggregated_signature # Create another block that includes our transaction dic_h = {11: (valid_program, aggsig)} new_blocks = bt.get_consecutive_blocks(test_constants, 1, blocks[:11], 10, b"1", coinbase_puzzlehash, dic_h) next_block = new_blocks[11] error = await full_node_1.blockchain._validate_transactions( next_block, next_block.get_fees_coin().amount) assert error is None
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_assert_coin_consumed(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] block2 = blocks[2] # This condition requires block2 coinbase to be spent block1_cvp = ConditionVarPair( ConditionOpcode.ASSERT_COIN_CONSUMED, block2.get_coinbase().name(), None, ) block1_dic = {block1_cvp.opcode: [block1_cvp]} block1_spend_bundle = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, block1.get_coinbase(), block1_dic) # This condition requires block1 coinbase to be spent block2_cvp = ConditionVarPair( ConditionOpcode.ASSERT_COIN_CONSUMED, block1.get_coinbase().name(), None, ) block2_dic = {block2_cvp.opcode: [block2_cvp]} block2_spend_bundle = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, block2.get_coinbase(), block2_dic) # Invalid block bundle assert block1_spend_bundle is not None solo_program = best_solution_program(block1_spend_bundle) aggsig = block1_spend_bundle.aggregated_signature # Create another block that includes our transaction dic_h = {11: (solo_program, aggsig)} invalid_new_blocks = bt.get_consecutive_blocks(test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h) # Try to validate that block 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_COIN_CONSUMED_FAILED # bundle_together contains both transactions bundle_together = SpendBundle.aggregate( [block1_spend_bundle, block2_spend_bundle]) valid_program = best_solution_program(bundle_together) aggsig = bundle_together.aggregated_signature # Create another block that includes our transaction dic_h = {11: (valid_program, aggsig)} new_blocks = bt.get_consecutive_blocks(test_constants, 1, blocks[:11], 10, b"1", coinbase_puzzlehash, dic_h) # Try to validate newly created block next_block = new_blocks[11] error = await full_node_1.blockchain._validate_transactions( next_block, next_block.get_fees_coin().amount) assert error is None
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