async def main(): rpc_port = 8555 self_hostname = "localhost" path = DEFAULT_ROOT_PATH config = load_config(path, "config.yaml") client = await FullNodeRpcClient.create(self_hostname, rpc_port, path, config) try: farmer_prefarm = ( await client.get_block_record_by_height(1)).reward_claims_incorporated[1] pool_prefarm = ( await client.get_block_record_by_height(1)).reward_claims_incorporated[0] pool_amounts = int(calculate_pool_reward(uint32(0)) / 2) farmer_amounts = int(calculate_base_farmer_reward(uint32(0)) / 2) print(farmer_prefarm.amount, farmer_amounts) assert farmer_amounts == farmer_prefarm.amount // 2 assert pool_amounts == pool_prefarm.amount // 2 address1 = "txch15gx26ndmacfaqlq8m0yajeggzceu7cvmaz4df0hahkukes695rss6lej7h" # Gene wallet (m/12381/8444/2/42): address2 = ( "txch1c2cguswhvmdyz9hr3q6hak2h6p9dw4rz82g4707k2xy2sarv705qcce4pn" # Mariano address (m/12381/8444/2/0) ) ph1 = decode_puzzle_hash(address1) ph2 = decode_puzzle_hash(address2) p_farmer_2 = Program.to( binutils.assemble( f"(q . ((51 0x{ph1.hex()} {farmer_amounts}) (51 0x{ph2.hex()} {farmer_amounts})))" )) p_pool_2 = Program.to( binutils.assemble( f"(q . ((51 0x{ph1.hex()} {pool_amounts}) (51 0x{ph2.hex()} {pool_amounts})))" )) p_solution = Program.to(binutils.assemble("()")) sb_farmer = SpendBundle( [CoinSolution(farmer_prefarm, p_farmer_2, p_solution)], G2Element()) sb_pool = SpendBundle( [CoinSolution(pool_prefarm, p_pool_2, p_solution)], G2Element()) print(sb_pool, sb_farmer) # res = await client.push_tx(sb_farmer) res = await client.push_tx(sb_pool) print(res) up = await client.get_coin_records_by_puzzle_hash( farmer_prefarm.puzzle_hash, True) uf = await client.get_coin_records_by_puzzle_hash( pool_prefarm.puzzle_hash, True) print(up) print(uf) finally: client.close()
async def main(): rpc_port = 8555 self_hostname = "localhost" path = DEFAULT_ROOT_PATH config = load_config(path, "config.yaml") client = await FullNodeRpcClient.create(self_hostname, rpc_port, path, config) try: farmer_prefarm = ( await client.get_block_record_by_height(1)).reward_claims_incorporated[1] pool_prefarm = ( await client.get_block_record_by_height(1)).reward_claims_incorporated[0] pool_amounts = int(calculate_pool_reward(uint32(0)) / 2) farmer_amounts = int(calculate_base_farmer_reward(uint32(0)) / 2) print(farmer_prefarm.amount, farmer_amounts) assert farmer_amounts == farmer_prefarm.amount // 2 assert pool_amounts == pool_prefarm.amount // 2 address1 = "txch1k50glwkdffp2mrqq64rsgjtxj4waphuf72stqayz4qqk6mj9hd4qp7lrek" # Gene wallet (m/12381/8444/2/51): address2 = "txch1430mtj60hvzyuyz4t45dyxwjdjsvphhl2fgreyry362reca4zpkszhjd3e" # farmer1 key (m/12381/8444/2/51) ph1 = decode_puzzle_hash(address1) ph2 = decode_puzzle_hash(address2) p_farmer_2 = Program.to( binutils.assemble( f"(q . ((51 0x{ph1.hex()} {farmer_amounts}) (51 0x{ph2.hex()} {farmer_amounts})))" )) p_pool_2 = Program.to( binutils.assemble( f"(q . ((51 0x{ph1.hex()} {pool_amounts}) (51 0x{ph2.hex()} {pool_amounts})))" )) p_solution = Program.to(binutils.assemble("()")) sb_farmer = SpendBundle( [CoinSolution(farmer_prefarm, p_farmer_2, p_solution)], G2Element()) sb_pool = SpendBundle( [CoinSolution(pool_prefarm, p_pool_2, p_solution)], G2Element()) print(sb_pool, sb_farmer) # res = await client.push_tx(sb_farmer) # res = await client.push_tx(sb_pool) # print(res) up = await client.get_coin_records_by_puzzle_hash( farmer_prefarm.puzzle_hash, True) uf = await client.get_coin_records_by_puzzle_hash( pool_prefarm.puzzle_hash, True) print(up) print(uf) finally: client.close()
async def main() -> None: rpc_port: uint16 = uint16(8555) self_hostname = "localhost" path = DEFAULT_ROOT_PATH config = load_config(path, "config.yaml") client = await FullNodeRpcClient.create(self_hostname, rpc_port, path, config) try: farmer_prefarm = ( await client.get_block_record_by_height(1)).reward_claims_incorporated[1] pool_prefarm = ( await client.get_block_record_by_height(1)).reward_claims_incorporated[0] pool_amounts = int(calculate_pool_reward(uint32(0)) / 2) farmer_amounts = int(calculate_base_farmer_reward(uint32(0)) / 2) print(farmer_prefarm.amount, farmer_amounts) assert farmer_amounts == farmer_prefarm.amount // 2 assert pool_amounts == pool_prefarm.amount // 2 address1 = "xch1rdatypul5c642jkeh4yp933zu3hw8vv8tfup8ta6zfampnyhjnusxdgns6" # Key 1 address2 = "xch1duvy5ur5eyj7lp5geetfg84cj2d7xgpxt7pya3lr2y6ke3696w9qvda66e" # Key 2 ph1 = decode_puzzle_hash(address1) ph2 = decode_puzzle_hash(address2) p_farmer_2 = Program.to( binutils.assemble( f"(q . ((51 0x{ph1.hex()} {farmer_amounts}) (51 0x{ph2.hex()} {farmer_amounts})))" )) p_pool_2 = Program.to( binutils.assemble( f"(q . ((51 0x{ph1.hex()} {pool_amounts}) (51 0x{ph2.hex()} {pool_amounts})))" )) p_solution = Program.to(binutils.assemble("()")) sb_farmer = SpendBundle( [CoinSolution(farmer_prefarm, p_farmer_2, p_solution)], G2Element()) sb_pool = SpendBundle( [CoinSolution(pool_prefarm, p_pool_2, p_solution)], G2Element()) print(sb_pool, sb_farmer) res = await client.push_tx(sb_farmer) # res = await client.push_tx(sb_pool) print(res) up = await client.get_coin_records_by_puzzle_hash( farmer_prefarm.puzzle_hash, True) uf = await client.get_coin_records_by_puzzle_hash( pool_prefarm.puzzle_hash, True) print(up) print(uf) finally: client.close()
def aggregate(cls, offers: List["Offer"]) -> "Offer": total_requested_payments: Dict[Optional[bytes32], List[NotarizedPayment]] = {} total_bundle = SpendBundle([], G2Element()) for offer in offers: # First check for any overlap in inputs total_inputs: Set[Coin] = { cs.coin for cs in total_bundle.coin_spends } offer_inputs: Set[Coin] = { cs.coin for cs in offer.bundle.coin_spends } if total_inputs & offer_inputs: raise ValueError("The aggregated offers overlap inputs") # Next, do the aggregation for tail, payments in offer.requested_payments.items(): if tail in total_requested_payments: total_requested_payments[tail].extend(payments) else: total_requested_payments[tail] = payments total_bundle = SpendBundle.aggregate([total_bundle, offer.bundle]) return cls(total_requested_payments, total_bundle)
def to_spend_bundle(self) -> SpendBundle: # Before we serialze this as a SpendBundle, we need to serialze the `requested_payments` as dummy CoinSpends additional_coin_spends: List[CoinSpend] = [] for tail_hash, payments in self.requested_payments.items(): puzzle_reveal: Program = construct_cat_puzzle( CAT_MOD, tail_hash, OFFER_MOD) if tail_hash else OFFER_MOD inner_solutions = [] nonces: List[bytes32] = [p.nonce for p in payments] for nonce in list( dict.fromkeys(nonces)): # dedup without messing with order nonce_payments: List[NotarizedPayment] = list( filter(lambda p: p.nonce == nonce, payments)) inner_solutions.append( (nonce, [np.as_condition_args() for np in nonce_payments])) additional_coin_spends.append( CoinSpend( Coin( ZERO_32, puzzle_reveal.get_tree_hash(), uint64(0), ), puzzle_reveal, Program.to(inner_solutions), )) return SpendBundle.aggregate([ SpendBundle(additional_coin_spends, G2Element()), self.bundle, ])
def do_inspect_spend_bundle_cmd(ctx, bundles, print_results=True, **kwargs): if kwargs and (len(kwargs['spend']) > 0) and (len(kwargs['aggsig']) > 0): spend_bundle_objs = [ SpendBundle( do_inspect_coin_spend_cmd(ctx, kwargs["spend"], print_results=False), AugSchemeMPL.aggregate([ G2Element(bytes.fromhex(sanitize_bytes(aggsig))) for aggsig in kwargs["aggsig"] ])) ] else: spend_bundle_objs = [] try: if type(bundles[0]) == str: spend_bundle_objs = streamable_load(SpendBundle, bundles) else: spend_bundle_objs = bundles except: print( "One or more of the specified objects was not a spend bundle") if print_results: inspect_callback(spend_bundle_objs, ctx, id_calc=(lambda e: e.name()), type='SpendBundle') else: return spend_bundle_objs
def launcher_conditions_and_spend_bundle( parent_coin_id: bytes32, launcher_amount: uint64, initial_singleton_inner_puzzle: Program, metadata: List[Tuple[str, str]], launcher_puzzle: Program = LAUNCHER_PUZZLE, ) -> Tuple[Program, bytes32, List[Program], SpendBundle]: launcher_puzzle_hash = launcher_puzzle.get_tree_hash() launcher_coin = Coin(parent_coin_id, launcher_puzzle_hash, launcher_amount) singleton_full_puzzle = SINGLETON_MOD.curry( SINGLETON_MOD_HASH, launcher_coin.name(), launcher_puzzle_hash, initial_singleton_inner_puzzle) singleton_full_puzzle_hash = singleton_full_puzzle.get_tree_hash() message_program = Program.to( [singleton_full_puzzle_hash, launcher_amount, metadata]) expected_announcement = Announcement(launcher_coin.name(), message_program.get_tree_hash()) expected_conditions = [] expected_conditions.append( Program.to( binutils.assemble( f"(0x{ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT.hex()} 0x{expected_announcement.name()})" ))) expected_conditions.append( Program.to( binutils.assemble( f"(0x{ConditionOpcode.CREATE_COIN.hex()} 0x{launcher_puzzle_hash} {launcher_amount})" ))) launcher_solution = Program.to( [singleton_full_puzzle_hash, launcher_amount, metadata]) coin_spend = CoinSpend(launcher_coin, launcher_puzzle, launcher_solution) spend_bundle = SpendBundle([coin_spend], G2Element()) lineage_proof = Program.to([parent_coin_id, launcher_amount]) return lineage_proof, launcher_coin.name( ), expected_conditions, spend_bundle
async def test_delegated_tail(self, setup_sim): sim, sim_client = setup_sim try: standard_acs = Program.to(1) standard_acs_ph: bytes32 = standard_acs.get_tree_hash() await sim.farm_block(standard_acs_ph) starting_coin: Coin = (await sim_client.get_coin_records_by_puzzle_hash(standard_acs_ph))[0].coin sk = PrivateKey.from_bytes(secret_exponent_for_index(1).to_bytes(32, "big")) tail: Program = DelegatedLimitations.construct([Program.to(sk.get_g1())]) cat_puzzle: Program = construct_cat_puzzle(CAT_MOD, tail.get_tree_hash(), acs) cat_ph: bytes32 = cat_puzzle.get_tree_hash() await sim_client.push_tx( SpendBundle( [CoinSpend(starting_coin, standard_acs, Program.to([[51, cat_ph, starting_coin.amount]]))], G2Element(), ) ) await sim.farm_block() # We're signing a different tail to use here name_as_program = Program.to(starting_coin.name()) new_tail: Program = GenesisById.construct([name_as_program]) checker_solution: Program = DelegatedLimitations.solve( [name_as_program], { "signed_program": { "identifier": "genesis_by_id", "args": [str(name_as_program)], }, "program_arguments": {}, }, ) signature: G2Element = AugSchemeMPL.sign(sk, new_tail.get_tree_hash()) await self.do_spend( sim, sim_client, tail, [(await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False))[0].coin], [NO_LINEAGE_PROOF], [ Program.to( [ [51, acs.get_tree_hash(), starting_coin.amount], [51, 0, -113, tail, checker_solution], ] ) ], (MempoolInclusionStatus.SUCCESS, None), signatures=[signature], limitations_solutions=[checker_solution], cost_str="Delegated Genesis", ) finally: await sim.close()
def test_vectors_invalid(): # Invalid inputs from https://github.com/algorand/bls_sigs_ref/blob/master/python-impl/serdesZ.py invalid_inputs_1 = [ # infinity points: too short "c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", # infinity points: not all zeros "c00000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000", # bad tags "3a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa", "7a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa", "fa0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa", # wrong length for compresed point "9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa", "9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaaaa", # invalid x-coord "9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa", # invalid elm of Fp --- equal to p (must be strictly less) "9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", ] invalid_inputs_2 = [ # infinity points: too short "c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", # infinity points: not all zeros "c00000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000", # bad tags "3a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "7a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "fa0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", # wrong length for compressed point "9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", # invalid x-coord "9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa7", # invalid elm of Fp --- equal to p (must be strictly less) "9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", ] for s in invalid_inputs_1: bytes_ = binascii.unhexlify(s) try: g1 = G1Element(bytes_) assert False, "Failed to disallow creation of G1 element." except Exception as e: pass for s in invalid_inputs_2: bytes_ = binascii.unhexlify(s) try: g2 = G2Element(bytes_) assert False, "Failed to disallow creation of G2 element." except Exception as e: pass
async def check_conditions( condition_solution: Program, expected_err: Optional[Err] = None, spend_reward_index: int = -2 ): blocks = initial_blocks() coin = list(blocks[spend_reward_index].get_included_reward_coins())[0] coin_solution = CoinSolution(coin, EASY_PUZZLE, condition_solution) spend_bundle = SpendBundle([coin_solution], G2Element()) # now let's try to create a block with the spend bundle and ensure that it doesn't validate await check_spend_bundle_validity(bt.constants, blocks, spend_bundle, expected_err=expected_err)
def test_only_odd_coins_0(): blocks = initial_blocks() farmed_coin = list(blocks[-1].get_included_reward_coins())[0] metadata = [("foo", "bar")] ANYONE_CAN_SPEND_PUZZLE = Program.to(1) launcher_amount = uint64(1) launcher_puzzle = LAUNCHER_PUZZLE launcher_puzzle_hash = launcher_puzzle.get_tree_hash() initial_singleton_puzzle = adaptor_for_singleton_inner_puzzle( ANYONE_CAN_SPEND_PUZZLE) lineage_proof, launcher_id, condition_list, launcher_spend_bundle = launcher_conditions_and_spend_bundle( farmed_coin.name(), launcher_amount, initial_singleton_puzzle, metadata, launcher_puzzle) conditions = Program.to(condition_list) coin_solution = CoinSolution(farmed_coin, ANYONE_CAN_SPEND_PUZZLE, conditions) spend_bundle = SpendBundle.aggregate( [launcher_spend_bundle, SpendBundle([coin_solution], G2Element())]) run = asyncio.get_event_loop().run_until_complete coins_added, coins_removed = run( check_spend_bundle_validity(bt.constants, blocks, spend_bundle)) coin_set_added = set([_.coin for _ in coins_added]) coin_set_removed = set([_.coin for _ in coins_removed]) launcher_coin = launcher_spend_bundle.coin_solutions[0].coin assert launcher_coin in coin_set_added assert launcher_coin in coin_set_removed assert farmed_coin in coin_set_removed # breakpoint() singleton_expected_puzzle_hash = singleton_puzzle_hash( launcher_id, launcher_puzzle_hash, initial_singleton_puzzle) expected_singleton_coin = Coin(launcher_coin.name(), singleton_expected_puzzle_hash, launcher_amount) assert expected_singleton_coin in coin_set_added # next up: spend the expected_singleton_coin # it's an adapted `ANYONE_CAN_SPEND_PUZZLE` # then try a bad lineage proof # then try writing two odd coins # then try writing zero odd coins # then, destroy the singleton with the -113 hack return 0
def generate_secure_bundle( self, selected_coins: List[Coin], announcements: List[Announcement], offered_amount: uint64, tail_str: Optional[str] = None, ) -> SpendBundle: announcement_assertions: List[List] = [[63, a.name()] for a in announcements] selected_coin_amount: int = sum([c.amount for c in selected_coins]) non_primaries: List[Coin] = [] if len( selected_coins) < 2 else selected_coins[1:] inner_solution: List[List] = [ [51, Offer.ph(), offered_amount], # Offered coin [51, acs_ph, uint64(selected_coin_amount - offered_amount)], # Change *announcement_assertions, ] if tail_str is None: bundle = SpendBundle( [ CoinSpend( selected_coins[0], acs, Program.to(inner_solution), ), *[ CoinSpend(c, acs, Program.to([])) for c in non_primaries ], ], G2Element(), ) else: spendable_cats: List[SpendableCAT] = [ SpendableCAT( c, str_to_tail_hash(tail_str), acs, Program.to([ [51, 0, -113, str_to_tail(tail_str), Program.to([])], # Use the TAIL rather than lineage *(inner_solution if c == selected_coins[0] else []), ]), ) for c in selected_coins ] bundle = unsigned_spend_bundle_for_spendable_cats( CAT_MOD, spendable_cats) return bundle
def spend_coin_to_singleton( puzzle_db: PuzzleDB, launcher_puzzle: Program, coin_store: CoinStore, now: CoinTimestamp) -> Tuple[List[Coin], List[CoinSpend]]: farmed_coin_amount = 100000 metadata = [("foo", "bar")] now = CoinTimestamp(10012300, 1) farmed_coin = coin_store.farm_coin(ANYONE_CAN_SPEND_PUZZLE.get_tree_hash(), now, amount=farmed_coin_amount) now.seconds += 500 now.height += 1 launcher_amount: uint64 = uint64(1) launcher_puzzle = LAUNCHER_PUZZLE launcher_puzzle_hash = launcher_puzzle.get_tree_hash() initial_singleton_puzzle = adaptor_for_singleton_inner_puzzle( ANYONE_CAN_SPEND_PUZZLE) launcher_id, condition_list, launcher_spend_bundle = launcher_conditions_and_spend_bundle( puzzle_db, farmed_coin.name(), launcher_amount, initial_singleton_puzzle, metadata, launcher_puzzle) conditions = Program.to(condition_list) coin_spend = CoinSpend(farmed_coin, ANYONE_CAN_SPEND_PUZZLE, conditions) spend_bundle = SpendBundle.aggregate( [launcher_spend_bundle, SpendBundle([coin_spend], G2Element())]) additions, removals = coin_store.update_coin_store_for_spend_bundle( spend_bundle, now, MAX_BLOCK_COST_CLVM, COST_PER_BYTE) launcher_coin = launcher_spend_bundle.coin_spends[0].coin assert_coin_spent(coin_store, launcher_coin) assert_coin_spent(coin_store, farmed_coin) # TODO: address hint error and remove ignore # error: Argument 1 to "singleton_puzzle" has incompatible type "bytes32"; expected "Program" [arg-type] singleton_expected_puzzle = singleton_puzzle( launcher_id, # type: ignore[arg-type] launcher_puzzle_hash, initial_singleton_puzzle, ) singleton_expected_puzzle_hash = singleton_expected_puzzle.get_tree_hash() expected_singleton_coin = Coin(launcher_coin.name(), singleton_expected_puzzle_hash, launcher_amount) assert_coin_spent(coin_store, expected_singleton_coin, is_spent=False) return additions, removals
def launcher_conditions_and_spend_bundle( puzzle_db: PuzzleDB, parent_coin_id: bytes32, launcher_amount: uint64, initial_singleton_inner_puzzle: Program, metadata: List[Tuple[str, str]], launcher_puzzle: Program, ) -> Tuple[bytes32, List[Program], SpendBundle]: puzzle_db.add_puzzle(launcher_puzzle) launcher_puzzle_hash = launcher_puzzle.get_tree_hash() launcher_coin = Coin(parent_coin_id, launcher_puzzle_hash, launcher_amount) # TODO: address hint error and remove ignore # error: Argument 1 to "singleton_puzzle" has incompatible type "bytes32"; expected "Program" [arg-type] singleton_full_puzzle = singleton_puzzle( launcher_coin.name(), # type: ignore[arg-type] launcher_puzzle_hash, initial_singleton_inner_puzzle, ) puzzle_db.add_puzzle(singleton_full_puzzle) singleton_full_puzzle_hash = singleton_full_puzzle.get_tree_hash() message_program = Program.to( [singleton_full_puzzle_hash, launcher_amount, metadata]) expected_announcement = Announcement(launcher_coin.name(), message_program.get_tree_hash()) expected_conditions = [] expected_conditions.append( Program.to( binutils.assemble( f"(0x{ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT.hex()} 0x{expected_announcement.name()})" ))) expected_conditions.append( Program.to( binutils.assemble( f"(0x{ConditionOpcode.CREATE_COIN.hex()} 0x{launcher_puzzle_hash} {launcher_amount})" ))) solution = solve_puzzle( puzzle_db, launcher_puzzle, destination_puzzle_hash=singleton_full_puzzle_hash, launcher_amount=launcher_amount, metadata=metadata, ) coin_spend = CoinSpend(launcher_coin, SerializedProgram.from_program(launcher_puzzle), solution) spend_bundle = SpendBundle([coin_spend], G2Element()) return launcher_coin.name(), expected_conditions, spend_bundle
async def create_absorb_transaction( node_rpc_client: FullNodeRpcClient, farmer_record: FarmerRecord, peak_height: uint32, reward_coin_records: List[CoinRecord], genesis_challenge: bytes32, ) -> SpendBundle: last_solution, last_state, _, _ = await get_and_validate_singleton_state_inner( node_rpc_client, farmer_record.launcher_id, farmer_record, peak_height, 0, None) launcher_coin_record: Optional[ CoinRecord] = await node_rpc_client.get_coin_record_by_name( farmer_record.launcher_id) assert launcher_coin_record is not None all_spends: List[CoinSolution] = [] for reward_coin_record in reward_coin_records: found_block_index: Optional[uint32] = None for block_index in range( reward_coin_record.confirmed_block_index, reward_coin_record.confirmed_block_index - 100, -1): if block_index < 0: break pool_parent = pool_parent_id(uint32(block_index), genesis_challenge) if pool_parent == reward_coin_record.coin.parent_coin_info: found_block_index = uint32(block_index) if not found_block_index: # The puzzle does not allow spending coins that are not a coinbase reward log.info( f"Received reward {reward_coin_record.coin} that is not a pool reward." ) continue absorb_spend: List[CoinSolution] = create_absorb_spend( last_solution, last_state, launcher_coin_record.coin, found_block_index, genesis_challenge, farmer_record.delay_time, farmer_record.delay_puzzle_hash, ) last_solution = absorb_spend[0] all_spends += absorb_spend # TODO(pool): handle the case where the cost exceeds the size of the block return SpendBundle(all_spends, G2Element())
def do_test_spend( puzzle_reveal: Program, solution: Program, payments: Iterable[Tuple[bytes32, int]], key_lookup: KeyTool, farm_time: CoinTimestamp = T1, spend_time: CoinTimestamp = T2, ) -> SpendBundle: """ This method will farm a coin paid to the hash of `puzzle_reveal`, then try to spend it with `solution`, and verify that the created coins correspond to `payments`. The `key_lookup` is used to create a signed version of the `SpendBundle`, although at this time, signatures are not verified. """ coin_db = CoinStore() puzzle_hash = puzzle_reveal.get_tree_hash() # farm it coin = coin_db.farm_coin(puzzle_hash, farm_time) # spend it coin_spend = CoinSpend(coin, puzzle_reveal, solution) spend_bundle = SpendBundle([coin_spend], G2Element()) coin_db.update_coin_store_for_spend_bundle(spend_bundle, spend_time, MAX_BLOCK_COST_CLVM, COST_PER_BYTE) # ensure all outputs are there for puzzle_hash, amount in payments: for coin in coin_db.coins_for_puzzle_hash(puzzle_hash): if coin.amount == amount: break else: assert 0 # make sure we can actually sign the solution signatures = [] for coin_spend in spend_bundle.coin_spends: signature = key_lookup.signature_for_solution(coin_spend, bytes([2] * 32)) signatures.append(signature) return SpendBundle(spend_bundle.coin_spends, AugSchemeMPL.aggregate(signatures))
def test_version_override(self): coin_spend = CoinSpend( COIN, OFFER_MOD, SOLUTION, ) spend_bundle = SpendBundle([coin_spend], G2Element()) compressed = compress_object_with_puzzles(bytes(spend_bundle), LATEST_VERSION) compressed_earlier = compress_object_with_puzzles( bytes(spend_bundle), 1) assert len(bytes(spend_bundle)) > len(bytes(compressed)) assert spend_bundle == SpendBundle.from_bytes( decompress_object_with_puzzles(compressed)) assert spend_bundle == SpendBundle.from_bytes( decompress_object_with_puzzles(compressed_earlier)) assert len(bytes(compressed_earlier)) > len(bytes(compressed))
def tx(info): j = json.dumps(info) m: Dict = eval(j) inputs: List = m.get("inputs") outputs: List = m.get("outputs") primaries = [] for o in outputs: output: Dict = o address: str = output.get("address") value: float = output.get("value") primaries.append({ "puzzlehash": decode_puzzle_hash(address), "amount": value }) spends: List[CoinSolution] = [] pks: List[str] = [] first_spend = True for i in inputs: input: Dict = i pk: str = input.get("pk") pks.append(pk) txid: Dict = eval(input.get("txId")) parentCoinInfo = txid.get("parentCoinInfo") puzzleHash = txid.get("puzzleHash") amount = txid.get("amount") pa = bytes32(bytes.fromhex(parentCoinInfo[2:])) pu = bytes32(bytes.fromhex(puzzleHash[2:])) a = uint64(amount) coin: Coin = Coin(pa, pu, a) child_sk: PrivateKey = PrivateKey.from_bytes(bytes.fromhex(pk)) child_public_key = child_sk.get_g1() puzzle = puzzle_for_pk(child_public_key) if first_spend: solution: Program = Wallet().make_solution(primaries=primaries) first_spend = False else: solution = Wallet().make_solution() spends.append(CoinSolution(coin, puzzle, solution)) spend_bundle: SpendBundle = SpendBundle(spends, G2Element()) # return json.dumps(spend_bundle.to_json_dict()) return sign_tx(pks, spend_bundle)
def sign_tx(pks: List[str], spend_bundle: SpendBundle): # This field is the ADDITIONAL_DATA found in the constants additional_data: bytes = bytes.fromhex( "ccd5bb71183532bff220ba46c268991a3ff07eb358e8255a65c30a2dce0e5fbb") puzzle_hash_to_sk: Dict[bytes32, PrivateKey] = {} for p in pks: child_sk: PrivateKey = PrivateKey.from_bytes(bytes.fromhex(p)) # master_private_key = PrivateKey.from_bytes( # bytes.fromhex(p)) # child_sk = master_sk_to_wallet_sk(master_private_key, 242) child_pk: G1Element = child_sk.get_g1() puzzle = puzzle_for_pk(child_pk) puzzle_hash = puzzle.get_tree_hash() puzzle_hash_to_sk[puzzle_hash] = child_sk aggregate_signature: G2Element = G2Element() for coin_solution in spend_bundle.coin_solutions: if coin_solution.coin.puzzle_hash not in puzzle_hash_to_sk: return sk: PrivateKey = puzzle_hash_to_sk[coin_solution.coin.puzzle_hash] synthetic_secret_key: PrivateKey = calculate_synthetic_secret_key( sk, DEFAULT_HIDDEN_PUZZLE_HASH) err, conditions_dict, cost = conditions_dict_for_solution( coin_solution.puzzle_reveal, coin_solution.solution, 11000000000) if err or conditions_dict is None: print( f"Sign transaction failed, con:{conditions_dict}, error: {err}" ) return pk_msgs = pkm_pairs_for_conditions_dict( conditions_dict, bytes(coin_solution.coin.name()), additional_data) assert len(pk_msgs) == 1 _, msg = pk_msgs[0] signature = AugSchemeMPL.sign(synthetic_secret_key, msg) aggregate_signature = AugSchemeMPL.aggregate( [aggregate_signature, signature]) new_spend_bundle = SpendBundle(spend_bundle.coin_solutions, aggregate_signature) # print(json.dumps(new_spend_bundle.to_json_dict())) return json.dumps(new_spend_bundle.to_json_dict())
async def test_genesis_by_puzhash(self, setup_sim): sim, sim_client = setup_sim try: standard_acs = Program.to(1) standard_acs_ph: bytes32 = standard_acs.get_tree_hash() await sim.farm_block(standard_acs_ph) starting_coin: Coin = (await sim_client.get_coin_records_by_puzzle_hash(standard_acs_ph))[0].coin tail: Program = GenesisByPuzhash.construct([Program.to(starting_coin.puzzle_hash)]) checker_solution: Program = GenesisByPuzhash.solve([], starting_coin.to_json_dict()) cat_puzzle: Program = construct_cat_puzzle(CAT_MOD, tail.get_tree_hash(), acs) cat_ph: bytes32 = cat_puzzle.get_tree_hash() await sim_client.push_tx( SpendBundle( [CoinSpend(starting_coin, standard_acs, Program.to([[51, cat_ph, starting_coin.amount]]))], G2Element(), ) ) await sim.farm_block() await self.do_spend( sim, sim_client, tail, [(await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False))[0].coin], [NO_LINEAGE_PROOF], [ Program.to( [ [51, acs.get_tree_hash(), starting_coin.amount], [51, 0, -113, tail, checker_solution], ] ) ], (MempoolInclusionStatus.SUCCESS, None), limitations_solutions=[checker_solution], cost_str="Genesis by Puzhash", ) finally: await sim.close()
def compute_coin_hints(cs: CoinSpend) -> List[bytes]: bundle = SpendBundle([cs], G2Element()) generator = simple_solution_generator(bundle) npc_result = get_name_puzzle_conditions( generator, INFINITE_COST, cost_per_byte=DEFAULT_CONSTANTS.COST_PER_BYTE, mempool_mode=False, height=DEFAULT_CONSTANTS.SOFT_FORK_HEIGHT, ) h_list = [] for npc in npc_result.npc_list: for opcode, conditions in npc.conditions: if opcode == ConditionOpcode.CREATE_COIN: for condition in conditions: if len(condition.vars) > 2 and condition.vars[2] != b"": h_list.append(condition.vars[2]) return h_list
from chia.types.blockchain_format.coin import Coin from chia.types.blockchain_format.program import Program, INFINITE_COST from chia.types.blockchain_format.sized_bytes import bytes32 from chia.types.condition_opcodes import ConditionOpcode from chia.types.spend_bundle import CoinSpend, SpendBundle from chia.util.condition_tools import conditions_dict_for_solution from chia.util.ints import uint64 from chia.wallet.puzzles.cc_loader import CC_MOD, LOCK_INNER_PUZZLE from chia.wallet.puzzles.genesis_by_coin_id_with_0 import ( genesis_coin_id_for_genesis_coin_checker, lineage_proof_for_coin, lineage_proof_for_genesis, lineage_proof_for_zero, ) NULL_SIGNATURE = G2Element() ANYONE_CAN_SPEND_PUZZLE = Program.to(1) # simply return the conditions # information needed to spend a cc # if we ever support more genesis conditions, like a re-issuable coin, # we may need also to save the `genesis_coin_mod` or its hash @dataclasses.dataclass class SpendableCC: coin: Coin genesis_coin_id: bytes32 inner_puzzle: Program lineage_proof: Program
def to_valid_spend(self, arbitrage_ph: Optional[bytes32] = None) -> SpendBundle: if not self.is_valid(): raise ValueError("Offer is currently incomplete") completion_spends: List[CoinSpend] = [] for tail_hash, payments in self.requested_payments.items(): offered_coins: List[Coin] = self.get_offered_coins()[tail_hash] # Because of CAT supply laws, we must specify a place for the leftovers to go arbitrage_amount: int = self.arbitrage()[tail_hash] all_payments: List[NotarizedPayment] = payments.copy() if arbitrage_amount > 0: assert arbitrage_amount is not None assert arbitrage_ph is not None all_payments.append( NotarizedPayment(arbitrage_ph, uint64(arbitrage_amount), [])) for coin in offered_coins: inner_solutions = [] if coin == offered_coins[0]: nonces: List[bytes32] = [p.nonce for p in all_payments] for nonce in list(dict.fromkeys( nonces)): # dedup without messing with order nonce_payments: List[NotarizedPayment] = list( filter(lambda p: p.nonce == nonce, all_payments)) inner_solutions.append( (nonce, [np.as_condition_args() for np in nonce_payments])) if tail_hash: # CATs have a special way to be solved so we have to do some calculation before getting the solution parent_spend: CoinSpend = list( filter( lambda cs: cs.coin.name() == coin.parent_coin_info, self.bundle.coin_spends))[0] parent_coin: Coin = parent_spend.coin matched, curried_args = match_cat_puzzle( parent_spend.puzzle_reveal.to_program()) assert matched _, _, inner_puzzle = curried_args spendable_cat = SpendableCAT( coin, tail_hash, OFFER_MOD, Program.to(inner_solutions), lineage_proof=LineageProof( parent_coin.parent_coin_info, inner_puzzle.get_tree_hash(), parent_coin.amount), ) solution: Program = ( unsigned_spend_bundle_for_spendable_cats( CAT_MOD, [spendable_cat ]).coin_spends[0].solution.to_program()) else: solution = Program.to(inner_solutions) completion_spends.append( CoinSpend( coin, construct_cat_puzzle(CAT_MOD, tail_hash, OFFER_MOD) if tail_hash else OFFER_MOD, solution, )) return SpendBundle.aggregate( [SpendBundle(completion_spends, G2Element()), self.bundle])
def test_elements(): b1 = BNWrapper([1, 2]) b2 = BNWrapper([3, 1, 4, 1, 5, 9]) i1 = int.from_bytes(bytes([1, 2]), byteorder="big") i2 = int.from_bytes(bytes([3, 1, 4, 1, 5, 9]), byteorder="big") g1 = G1Element.generator() g2 = G2Element.generator() u1 = G1Element.infinity() # unity u2 = G2Element.infinity() # Does not allow construction try: i = G1Element() assert False except Exception: pass try: i = G2Element() assert False except Exception: pass x1 = g1 * b1 x2 = g1 * b2 y1 = g2 * b1 y2 = g2 * b2 # Implicit conversion from python ints to BNWrapperWrapperWrapper assert x1 == g1 * i1 == i1 * g1 assert x2 == g1 * i2 == i2 * g1 assert y1 == g2 * i1 == i1 * g2 assert y2 == g2 * i2 == i2 * g2 # G1 assert x1 != x2 assert x1 * b1 == b1 * x1 assert x1 * b1 != x1 * b2 assert x1 + u1 == x1 assert x1 + x2 == x2 + x1 assert x1 + x1.negate() == u1 assert x1 == G1Element(bytes(x1)) copy = deepcopy(x1) assert x1 == copy x1 += x2 assert x1 != copy # G2 assert y1 != y2 assert y1 * b1 == b1 * y1 assert y1 * b1 != y1 * b2 assert y1 + u2 == y1 assert y1 + y2 == y2 + y1 assert y1 + y1.negate() == u2 assert y1 == G2Element(bytes(y1)) copy = deepcopy(y1) assert y1 == copy y1 += y2 assert y1 != copy # pairing operation pair = x1 & y1 assert pair != x1 & y2 assert pair != x2 & y1 assert pair == x1.pair(y1) assert pair == GTElement(bytes(pair)) copy = deepcopy(pair) assert pair == copy pair = None assert pair != copy
def test_lifecycle_with_coinstore_as_wallet(): PUZZLE_DB = PuzzleDB() interested_singletons = [] ####### # farm a coin coin_store = CoinStore(int.from_bytes(POOL_REWARD_PREFIX_MAINNET, "big")) now = CoinTimestamp(10012300, 1) DELAY_SECONDS = 86400 DELAY_PUZZLE_HASH = bytes([0] * 32) ####### # spend coin to a singleton additions, removals = spend_coin_to_singleton(PUZZLE_DB, LAUNCHER_PUZZLE, coin_store, now) assert len(list(coin_store.all_unspent_coins())) == 1 new_singletons = find_interesting_singletons(PUZZLE_DB, removals) interested_singletons.extend(new_singletons) assert len(interested_singletons) == 1 SINGLETON_WALLET = interested_singletons[0] ####### # farm a `p2_singleton` pool_reward_puzzle_hash = p2_singleton_puzzle_hash_for_launcher( PUZZLE_DB, SINGLETON_WALLET.launcher_id, SINGLETON_WALLET.launcher_puzzle_hash, DELAY_SECONDS, DELAY_PUZZLE_HASH) farmed_coin = coin_store.farm_coin(pool_reward_puzzle_hash, now) now.seconds += 500 now.height += 1 p2_singleton_coins = filter_p2_singleton(PUZZLE_DB, SINGLETON_WALLET, [farmed_coin]) assert p2_singleton_coins == [farmed_coin] assert len(list(coin_store.all_unspent_coins())) == 2 ####### # now collect the `p2_singleton` using the singleton for coin in p2_singleton_coins: p2_singleton_coin_spend, singleton_conditions = claim_p2_singleton( PUZZLE_DB, SINGLETON_WALLET, coin) coin_spend = SINGLETON_WALLET.coin_spend_for_conditions( PUZZLE_DB, conditions=singleton_conditions) spend_bundle = SpendBundle([coin_spend, p2_singleton_coin_spend], G2Element()) additions, removals = coin_store.update_coin_store_for_spend_bundle( spend_bundle, now, MAX_BLOCK_COST_CLVM, COST_PER_BYTE) now.seconds += 500 now.height += 1 SINGLETON_WALLET.update_state(PUZZLE_DB, removals) assert len(list(coin_store.all_unspent_coins())) == 1 ####### # farm and collect another `p2_singleton` pool_reward_puzzle_hash = p2_singleton_puzzle_hash_for_launcher( PUZZLE_DB, SINGLETON_WALLET.launcher_id, SINGLETON_WALLET.launcher_puzzle_hash, DELAY_SECONDS, DELAY_PUZZLE_HASH) farmed_coin = coin_store.farm_coin(pool_reward_puzzle_hash, now) now.seconds += 500 now.height += 1 p2_singleton_coins = filter_p2_singleton(PUZZLE_DB, SINGLETON_WALLET, [farmed_coin]) assert p2_singleton_coins == [farmed_coin] assert len(list(coin_store.all_unspent_coins())) == 2 for coin in p2_singleton_coins: p2_singleton_coin_spend, singleton_conditions = claim_p2_singleton( PUZZLE_DB, SINGLETON_WALLET, coin) coin_spend = SINGLETON_WALLET.coin_spend_for_conditions( PUZZLE_DB, conditions=singleton_conditions) spend_bundle = SpendBundle([coin_spend, p2_singleton_coin_spend], G2Element()) additions, removals = coin_store.update_coin_store_for_spend_bundle( spend_bundle, now, MAX_BLOCK_COST_CLVM, COST_PER_BYTE) now.seconds += 500 now.height += 1 SINGLETON_WALLET.update_state(PUZZLE_DB, removals) assert len(list(coin_store.all_unspent_coins())) == 1 ####### # loan the singleton to a pool # puzzle_for_loan_singleton_to_pool( # pool_puzzle_hash, p2_singleton_puzzle_hash, owner_public_key, pool_reward_prefix, relative_lock_height) # calculate the series owner_public_key = bytes(create_throwaway_pubkey(b"foo")) pool_puzzle_hash = Program.to(bytes( create_throwaway_pubkey(b""))).get_tree_hash() pool_reward_prefix = POOL_REWARD_PREFIX_MAINNET relative_lock_height = 1440 pool_escaping_puzzle = POOL_WAITINGROOM_MOD.curry(pool_puzzle_hash, pool_reward_puzzle_hash, owner_public_key, pool_reward_prefix, relative_lock_height) pool_escaping_puzzle_hash = pool_escaping_puzzle.get_tree_hash() pool_member_puzzle = POOL_MEMBER_MOD.curry( pool_puzzle_hash, pool_reward_puzzle_hash, owner_public_key, pool_reward_prefix, pool_escaping_puzzle_hash, ) pool_member_puzzle_hash = pool_member_puzzle.get_tree_hash() PUZZLE_DB.add_puzzle(pool_escaping_puzzle) PUZZLE_DB.add_puzzle( singleton_puzzle(SINGLETON_WALLET.launcher_id, SINGLETON_WALLET.launcher_puzzle_hash, pool_escaping_puzzle)) PUZZLE_DB.add_puzzle(pool_member_puzzle) full_puzzle = singleton_puzzle(SINGLETON_WALLET.launcher_id, SINGLETON_WALLET.launcher_puzzle_hash, pool_member_puzzle) PUZZLE_DB.add_puzzle(full_puzzle) conditions = [ Program.to([ConditionOpcode.CREATE_COIN, pool_member_puzzle_hash, 1]) ] singleton_coin_spend = SINGLETON_WALLET.coin_spend_for_conditions( PUZZLE_DB, conditions=conditions) spend_bundle = SpendBundle([singleton_coin_spend], G2Element()) additions, removals = coin_store.update_coin_store_for_spend_bundle( spend_bundle, now, MAX_BLOCK_COST_CLVM, COST_PER_BYTE) assert len(list(coin_store.all_unspent_coins())) == 1 SINGLETON_WALLET.update_state(PUZZLE_DB, removals) ####### # farm a `p2_singleton` pool_reward_puzzle_hash = p2_singleton_puzzle_hash_for_launcher( PUZZLE_DB, SINGLETON_WALLET.launcher_id, SINGLETON_WALLET.launcher_puzzle_hash, DELAY_SECONDS, DELAY_PUZZLE_HASH) farmed_coin = coin_store.farm_coin(pool_reward_puzzle_hash, now) now.seconds += 500 now.height += 1 p2_singleton_coins = filter_p2_singleton(PUZZLE_DB, SINGLETON_WALLET, [farmed_coin]) assert p2_singleton_coins == [farmed_coin] assert len(list(coin_store.all_unspent_coins())) == 2 ####### # now collect the `p2_singleton` for the pool for coin in p2_singleton_coins: p2_singleton_coin_spend, singleton_conditions = claim_p2_singleton( PUZZLE_DB, SINGLETON_WALLET, coin) coin_spend = SINGLETON_WALLET.coin_spend_for_conditions( PUZZLE_DB, pool_member_spend_type="claim-p2-nft", pool_reward_amount=p2_singleton_coin_spend.coin.amount, pool_reward_height=now.height - 1, ) spend_bundle = SpendBundle([coin_spend, p2_singleton_coin_spend], G2Element()) spend_bundle.debug() additions, removals = coin_store.update_coin_store_for_spend_bundle( spend_bundle, now, MAX_BLOCK_COST_CLVM, COST_PER_BYTE) now.seconds += 500 now.height += 1 SINGLETON_WALLET.update_state(PUZZLE_DB, removals) assert len(list(coin_store.all_unspent_coins())) == 2 ####### # spend the singleton into the "leaving the pool" state coin_spend = SINGLETON_WALLET.coin_spend_for_conditions( PUZZLE_DB, pool_member_spend_type="to-waiting-room", key_value_list=Program.to([("foo", "bar")])) spend_bundle = SpendBundle([coin_spend], G2Element()) additions, removals = coin_store.update_coin_store_for_spend_bundle( spend_bundle, now, MAX_BLOCK_COST_CLVM, COST_PER_BYTE) now.seconds += 500 now.height += 1 change_count = SINGLETON_WALLET.update_state(PUZZLE_DB, removals) assert change_count == 1 assert len(list(coin_store.all_unspent_coins())) == 2 ####### # farm a `p2_singleton` pool_reward_puzzle_hash = p2_singleton_puzzle_hash_for_launcher( PUZZLE_DB, SINGLETON_WALLET.launcher_id, SINGLETON_WALLET.launcher_puzzle_hash, DELAY_SECONDS, DELAY_PUZZLE_HASH) farmed_coin = coin_store.farm_coin(pool_reward_puzzle_hash, now) now.seconds += 500 now.height += 1 p2_singleton_coins = filter_p2_singleton(PUZZLE_DB, SINGLETON_WALLET, [farmed_coin]) assert p2_singleton_coins == [farmed_coin] assert len(list(coin_store.all_unspent_coins())) == 3 ####### # now collect the `p2_singleton` for the pool for coin in p2_singleton_coins: p2_singleton_coin_spend, singleton_conditions = claim_p2_singleton( PUZZLE_DB, SINGLETON_WALLET, coin) coin_spend = SINGLETON_WALLET.coin_spend_for_conditions( PUZZLE_DB, pool_leaving_spend_type="claim-p2-nft", pool_reward_amount=p2_singleton_coin_spend.coin.amount, pool_reward_height=now.height - 1, ) spend_bundle = SpendBundle([coin_spend, p2_singleton_coin_spend], G2Element()) additions, removals = coin_store.update_coin_store_for_spend_bundle( spend_bundle, now, MAX_BLOCK_COST_CLVM, COST_PER_BYTE) now.seconds += 500 now.height += 1 SINGLETON_WALLET.update_state(PUZZLE_DB, removals) assert len(list(coin_store.all_unspent_coins())) == 3 ####### # now finish leaving the pool initial_singleton_puzzle = adaptor_for_singleton_inner_puzzle( ANYONE_CAN_SPEND_PUZZLE) coin_spend = SINGLETON_WALLET.coin_spend_for_conditions( PUZZLE_DB, pool_leaving_spend_type="exit-waiting-room", key_value_list=[("foo1", "bar2"), ("foo2", "baz5")], destination_puzzle_hash=initial_singleton_puzzle.get_tree_hash(), ) spend_bundle = SpendBundle([coin_spend], G2Element()) full_puzzle = singleton_puzzle(SINGLETON_WALLET.launcher_id, SINGLETON_WALLET.launcher_puzzle_hash, initial_singleton_puzzle) PUZZLE_DB.add_puzzle(full_puzzle) try: additions, removals = coin_store.update_coin_store_for_spend_bundle( spend_bundle, now, MAX_BLOCK_COST_CLVM, COST_PER_BYTE) assert 0 except BadSpendBundleError as ex: assert ex.args[ 0] == "condition validation failure Err.ASSERT_HEIGHT_RELATIVE_FAILED" now.seconds += 350000 now.height += 1445 additions, removals = coin_store.update_coin_store_for_spend_bundle( spend_bundle, now, MAX_BLOCK_COST_CLVM, COST_PER_BYTE) SINGLETON_WALLET.update_state(PUZZLE_DB, removals) assert len(list(coin_store.all_unspent_coins())) == 3 ####### # now spend to oblivion with the `-113` hack coin_spend = SINGLETON_WALLET.coin_spend_for_conditions( PUZZLE_DB, conditions=[[ConditionOpcode.CREATE_COIN, 0, -113]]) spend_bundle = SpendBundle([coin_spend], G2Element()) spend_bundle.debug() additions, removals = coin_store.update_coin_store_for_spend_bundle( spend_bundle, now, MAX_BLOCK_COST_CLVM, COST_PER_BYTE) update_count = SINGLETON_WALLET.update_state(PUZZLE_DB, removals) assert update_count == 0 assert len(list(coin_store.all_unspent_coins())) == 2 return 0
async def test_singleton_top_layer(self): try: # START TESTS # Generate starting info key_lookup = KeyTool() pk: G1Element = public_key_for_index(1, key_lookup) starting_puzzle: Program = p2_delegated_puzzle_or_hidden_puzzle.puzzle_for_pk( pk) # noqa adapted_puzzle: Program = singleton_top_layer.adapt_inner_to_singleton( starting_puzzle) # noqa adapted_puzzle_hash: bytes32 = adapted_puzzle.get_tree_hash() # Get our starting standard coin created START_AMOUNT: uint64 = 1023 sim = await SpendSim.create() sim_client = SimClient(sim) await sim.farm_block(starting_puzzle.get_tree_hash()) starting_coin: Coin = await sim_client.get_coin_records_by_puzzle_hash( starting_puzzle.get_tree_hash()) starting_coin = starting_coin[0].coin comment: List[Tuple[str, str]] = [("hello", "world")] # LAUNCHING # Try to create an even singleton (driver test) try: conditions, launcher_coinsol = singleton_top_layer.launch_conditions_and_coinsol( # noqa starting_coin, adapted_puzzle, comment, (START_AMOUNT - 1)) raise AssertionError("This should fail due to an even amount") except ValueError as msg: assert str( msg) == "Coin amount cannot be even. Subtract one mojo." conditions, launcher_coinsol = singleton_top_layer.launch_conditions_and_coinsol( # noqa starting_coin, adapted_puzzle, comment, START_AMOUNT) # Creating solution for standard transaction delegated_puzzle: Program = p2_conditions.puzzle_for_conditions( conditions) # noqa full_solution: Program = p2_delegated_puzzle_or_hidden_puzzle.solution_for_conditions( conditions) # noqa starting_coinsol = CoinSpend( starting_coin, starting_puzzle, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, starting_coin, delegated_puzzle, [starting_coinsol, launcher_coinsol], ) # EVE singleton_eve: Coin = (await sim.all_non_reward_coins())[0] launcher_coin: Coin = singleton_top_layer.generate_launcher_coin( starting_coin, START_AMOUNT, ) launcher_id: bytes32 = launcher_coin.name() # This delegated puzzle just recreates the coin exactly delegated_puzzle: Program = Program.to(( 1, [[ ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, singleton_eve.amount, ]], )) inner_solution: Program = Program.to([[], delegated_puzzle, []]) # Generate the lineage proof we will need from the launcher coin lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( launcher_coinsol) # noqa puzzle_reveal: Program = singleton_top_layer.puzzle_for_singleton( launcher_id, adapted_puzzle, ) full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, singleton_eve.amount, inner_solution, ) singleton_eve_coinsol = CoinSpend( singleton_eve, puzzle_reveal, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, singleton_eve, delegated_puzzle, [singleton_eve_coinsol], ) # POST-EVE singleton: Coin = (await sim.all_non_reward_coins())[0] # Same delegated_puzzle / inner_solution. We're just recreating ourself lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( singleton_eve_coinsol) # noqa # Same puzzle_reveal too full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, singleton.amount, inner_solution, ) singleton_coinsol = CoinSpend( singleton, puzzle_reveal, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, singleton, delegated_puzzle, [singleton_coinsol], ) # CLAIM A P2_SINGLETON singleton_child: Coin = (await sim.all_non_reward_coins())[0] p2_singleton_puz: Program = singleton_top_layer.pay_to_singleton_puzzle( launcher_id) p2_singleton_ph: bytes32 = p2_singleton_puz.get_tree_hash() ARBITRARY_AMOUNT: uint64 = 1379 await sim.farm_block(p2_singleton_ph) p2_singleton_coin: Coin = await sim_client.get_coin_records_by_puzzle_hash( p2_singleton_ph) p2_singleton_coin = p2_singleton_coin[0].coin assertion, announcement, claim_coinsol = singleton_top_layer.claim_p2_singleton( p2_singleton_coin, adapted_puzzle_hash, launcher_id, ) delegated_puzzle: Program = Program.to(( 1, [ [ ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, singleton_eve.amount ], assertion, announcement, ], )) inner_solution: Program = Program.to([[], delegated_puzzle, []]) lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( singleton_coinsol) puzzle_reveal: Program = singleton_top_layer.puzzle_for_singleton( launcher_id, adapted_puzzle, ) full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, singleton_eve.amount, inner_solution, ) singleton_claim_coinsol = CoinSpend( singleton_child, puzzle_reveal, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, singleton_child, delegated_puzzle, [singleton_claim_coinsol, claim_coinsol]) # CLAIM A P2_SINGLETON_OR_DELAYED singleton_child: Coin = (await sim.all_non_reward_coins())[0] DELAY_TIME: uint64 = 1 DELAY_PH: bytes32 = adapted_puzzle_hash p2_singleton_puz: Program = singleton_top_layer.pay_to_singleton_or_delay_puzzle( launcher_id, DELAY_TIME, DELAY_PH, ) p2_singleton_ph: bytes32 = p2_singleton_puz.get_tree_hash() ARBITRARY_AMOUNT: uint64 = 1379 await sim.farm_block(p2_singleton_ph) p2_singleton_coin: Coin = await sim_client.get_coin_records_by_puzzle_hash( p2_singleton_ph) p2_singleton_coin = p2_singleton_coin[0].coin assertion, announcement, claim_coinsol = singleton_top_layer.claim_p2_singleton( p2_singleton_coin, adapted_puzzle_hash, launcher_id, DELAY_TIME, DELAY_PH, ) delegated_puzzle: Program = Program.to(( 1, [ [ ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, singleton_eve.amount ], assertion, announcement, ], )) inner_solution: Program = Program.to([[], delegated_puzzle, []]) lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( singleton_claim_coinsol) puzzle_reveal: Program = singleton_top_layer.puzzle_for_singleton( launcher_id, adapted_puzzle, ) full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, singleton_eve.amount, inner_solution, ) delay_claim_coinsol = CoinSpend( singleton_child, puzzle_reveal, full_solution, ) # Save the height so we can rewind after this save_height: uint64 = ( sim.get_height() ) # The last coin solution before this point is singleton_claim_coinsol await self.make_and_spend_bundle( sim, sim_client, singleton_child, delegated_puzzle, [delay_claim_coinsol, claim_coinsol]) # TRY TO SPEND AWAY TOO SOON (Negative Test) await sim.rewind(save_height) to_delay_ph_coinsol: CoinSpend = singleton_top_layer.spend_to_delayed_puzzle( p2_singleton_coin, ARBITRARY_AMOUNT, launcher_id, DELAY_TIME, DELAY_PH, ) result, error = await sim_client.push_tx( SpendBundle([to_delay_ph_coinsol], G2Element())) assert error == Err.ASSERT_SECONDS_RELATIVE_FAILED # SPEND TO DELAYED PUZZLE HASH await sim.rewind(save_height) sim.pass_time(10000005) sim.pass_blocks(100) await sim_client.push_tx( SpendBundle([to_delay_ph_coinsol], G2Element())) # CREATE MULTIPLE ODD CHILDREN (Negative Test) singleton_child: Coin = (await sim.all_non_reward_coins())[0] delegated_puzzle: Program = Program.to(( 1, [ [ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, 3], [ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, 7], ], )) inner_solution: Program = Program.to([[], delegated_puzzle, []]) lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( singleton_claim_coinsol) puzzle_reveal: Program = singleton_top_layer.puzzle_for_singleton( launcher_id, adapted_puzzle, ) full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, singleton_child.amount, inner_solution) multi_odd_coinsol = CoinSpend( singleton_child, puzzle_reveal, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, singleton_child, delegated_puzzle, [multi_odd_coinsol], ex_error=Err.GENERATOR_RUNTIME_ERROR, fail_msg="Too many odd children were allowed", ) # CREATE NO ODD CHILDREN (Negative Test) delegated_puzzle: Program = Program.to(( 1, [ [ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, 4], [ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, 10], ], )) inner_solution: Program = Program.to([[], delegated_puzzle, []]) lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( singleton_claim_coinsol) puzzle_reveal: Program = singleton_top_layer.puzzle_for_singleton( launcher_id, adapted_puzzle, ) full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, singleton_child.amount, inner_solution) no_odd_coinsol = CoinSpend( singleton_child, puzzle_reveal, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, singleton_child, delegated_puzzle, [no_odd_coinsol], ex_error=Err.GENERATOR_RUNTIME_ERROR, fail_msg="Need at least one odd child", ) # ATTEMPT TO CREATE AN EVEN SINGLETON (Negative test) await sim.rewind(save_height) delegated_puzzle: Program = Program.to(( 1, [ [ ConditionOpcode.CREATE_COIN, singleton_child.puzzle_hash, 2, ], [ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, 1], ], )) inner_solution: Program = Program.to([[], delegated_puzzle, []]) lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( singleton_claim_coinsol) puzzle_reveal: Program = singleton_top_layer.puzzle_for_singleton( launcher_id, adapted_puzzle, ) full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, singleton_child.amount, inner_solution) singleton_even_coinsol = CoinSpend( singleton_child, puzzle_reveal, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, singleton_child, delegated_puzzle, [singleton_even_coinsol], ) # Now try a perfectly innocent spend evil_coin: Coin = (await sim.all_non_reward_coins())[0] delegated_puzzle: Program = Program.to(( 1, [ [ ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, 1, ], ], )) inner_solution: Program = Program.to([[], delegated_puzzle, []]) lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( singleton_even_coinsol) # noqa puzzle_reveal: Program = singleton_top_layer.puzzle_for_singleton( launcher_id, adapted_puzzle, ) full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, 1, inner_solution, ) evil_coinsol = CoinSpend( evil_coin, puzzle_reveal, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, evil_coin, delegated_puzzle, [evil_coinsol], ex_error=Err.ASSERT_MY_COIN_ID_FAILED, fail_msg="This coin is even!", ) # MELTING # Remember, we're still spending singleton_child await sim.rewind(save_height) conditions = [ singleton_top_layer.MELT_CONDITION, [ ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, (singleton_child.amount - 1), ], ] delegated_puzzle: Program = p2_conditions.puzzle_for_conditions( conditions) inner_solution: Program = p2_delegated_puzzle_or_hidden_puzzle.solution_for_conditions( conditions) lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( singleton_claim_coinsol) puzzle_reveal: Program = singleton_top_layer.puzzle_for_singleton( launcher_id, adapted_puzzle, ) full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, singleton_child.amount, inner_solution) melt_coinsol = CoinSpend( singleton_child, puzzle_reveal, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, singleton_child, delegated_puzzle, [melt_coinsol], ) melted_coin: Coin = (await sim.all_non_reward_coins())[0] assert melted_coin.puzzle_hash == adapted_puzzle_hash finally: await sim.close()
async def create_absorb_transaction( self, farmer_record: FarmerRecord, singleton_coin: Coin, reward_coin_records: List[CoinRecord]) -> SpendBundle: # We assume that the farmer record singleton state is updated to the latest escape_inner_puzzle: Program = POOL_ESCAPING_MOD.curry( farmer_record.pool_puzzle_hash, self.relative_lock_height, bytes(farmer_record.owner_public_key), farmer_record.p2_singleton_puzzle_hash, ) committed_inner_puzzle: Program = POOL_COMMITED_MOD.curry( farmer_record.pool_puzzle_hash, escape_inner_puzzle.get_tree_hash(), farmer_record.p2_singleton_puzzle_hash, bytes(farmer_record.owner_public_key), ) aggregate_spend_bundle: SpendBundle = SpendBundle([], G2Element()) for reward_coin_record in reward_coin_records: found_block_index: Optional[uint32] = None for block_index in range( reward_coin_record.confirmed_block_index, reward_coin_record.confirmed_block_index - 100, -1): if block_index < 0: break pool_parent = pool_parent_id(uint32(block_index), self.constants.GENESIS_CHALLENGE) if pool_parent == reward_coin_record.coin.parent_coin_info: found_block_index = uint32(block_index) if not found_block_index: self.log.info( f"Received reward {reward_coin_record.coin} that is not a pool reward." ) singleton_full = SINGLETON_MOD.curry( singleton_mod_hash, farmer_record.singleton_genesis, committed_inner_puzzle) inner_sol = Program.to([ 0, singleton_full.get_tree_hash(), singleton_coin.amount, reward_coin_record.amount, found_block_index ]) full_sol = Program.to([ farmer_record.singleton_genesis, singleton_coin.amount, inner_sol ]) new_spend = SpendBundle( [ CoinSolution( singleton_coin, SerializedProgram.from_bytes(bytes(singleton_full)), full_sol) ], G2Element(), ) # TODO(pool): handle the case where the cost exceeds the size of the block aggregate_spend_bundle = SpendBundle.aggregate( [aggregate_spend_bundle, new_spend]) singleton_coin = await self.get_next_singleton_coin(new_spend) cost, result = singleton_full.run_with_cost( INFINITE_COST, full_sol) self.log.info(f"Cost: {cost}, result {result}") return aggregate_spend_bundle
def test_pool_lifecycle(self): # START TESTS # Generate starting info key_lookup = KeyTool() sk: PrivateKey = PrivateKey.from_bytes( secret_exponent_for_index(1).to_bytes(32, "big"), ) pk: G1Element = G1Element.from_bytes( public_key_for_index(1, key_lookup)) starting_puzzle: Program = puzzle_for_pk(pk) starting_ph: bytes32 = starting_puzzle.get_tree_hash() # Get our starting standard coin created START_AMOUNT: uint64 = 1023 coin_db = CoinStore() time = CoinTimestamp(10000000, 1) coin_db.farm_coin(starting_ph, time, START_AMOUNT) starting_coin: Coin = next(coin_db.all_unspent_coins()) # LAUNCHING # Create the escaping inner puzzle GENESIS_CHALLENGE = bytes32.fromhex( "ccd5bb71183532bff220ba46c268991a3ff07eb358e8255a65c30a2dce0e5fbb") launcher_coin = singleton_top_layer.generate_launcher_coin( starting_coin, START_AMOUNT, ) DELAY_TIME = uint64(60800) DELAY_PH = starting_ph launcher_id = launcher_coin.name() relative_lock_height: uint32 = uint32(5000) # use a dummy pool state pool_state = PoolState( owner_pubkey=pk, pool_url="", relative_lock_height=relative_lock_height, state=3, # farming to pool target_puzzle_hash=starting_ph, version=1, ) # create a new dummy pool state for travelling target_pool_state = PoolState( owner_pubkey=pk, pool_url="", relative_lock_height=relative_lock_height, state=2, # Leaving pool target_puzzle_hash=starting_ph, version=1, ) # Standard format comment comment = Program.to([("p", bytes(pool_state)), ("t", DELAY_TIME), ("h", DELAY_PH)]) pool_wr_innerpuz: bytes32 = create_waiting_room_inner_puzzle( starting_ph, relative_lock_height, pk, launcher_id, GENESIS_CHALLENGE, DELAY_TIME, DELAY_PH, ) pool_wr_inner_hash = pool_wr_innerpuz.get_tree_hash() pooling_innerpuz: Program = create_pooling_inner_puzzle( starting_ph, pool_wr_inner_hash, pk, launcher_id, GENESIS_CHALLENGE, DELAY_TIME, DELAY_PH, ) # Driver tests assert is_pool_singleton_inner_puzzle(pooling_innerpuz) assert is_pool_singleton_inner_puzzle(pool_wr_innerpuz) assert get_pubkey_from_member_inner_puzzle(pooling_innerpuz) == pk # Generating launcher information conditions, launcher_coinsol = singleton_top_layer.launch_conditions_and_coinsol( starting_coin, pooling_innerpuz, comment, START_AMOUNT) # Creating solution for standard transaction delegated_puzzle: Program = puzzle_for_conditions(conditions) full_solution: Program = solution_for_conditions(conditions) starting_coinsol = CoinSolution( starting_coin, starting_puzzle, full_solution, ) # Create the spend bundle sig: G2Element = sign_delegated_puz(delegated_puzzle, starting_coin) spend_bundle = SpendBundle( [starting_coinsol, launcher_coinsol], sig, ) # Spend it! coin_db.update_coin_store_for_spend_bundle( spend_bundle, time, DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM, ) # Test that we can retrieve the extra data assert get_delayed_puz_info_from_launcher_spend(launcher_coinsol) == ( DELAY_TIME, DELAY_PH) assert solution_to_extra_data(launcher_coinsol) == pool_state # TEST TRAVEL AFTER LAUNCH # fork the state fork_coin_db: CoinStore = copy.deepcopy(coin_db) post_launch_coinsol, _ = create_travel_spend( launcher_coinsol, launcher_coin, pool_state, target_pool_state, GENESIS_CHALLENGE, DELAY_TIME, DELAY_PH, ) # Spend it! fork_coin_db.update_coin_store_for_spend_bundle( SpendBundle([post_launch_coinsol], G2Element()), time, DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM, ) # HONEST ABSORB time = CoinTimestamp(10000030, 2) # create the farming reward p2_singleton_puz: Program = create_p2_singleton_puzzle( SINGLETON_MOD_HASH, launcher_id, DELAY_TIME, DELAY_PH, ) p2_singleton_ph: bytes32 = p2_singleton_puz.get_tree_hash() assert uncurry_pool_waitingroom_inner_puzzle(pool_wr_innerpuz) == ( starting_ph, relative_lock_height, pk, p2_singleton_ph, ) assert launcher_id_to_p2_puzzle_hash(launcher_id, DELAY_TIME, DELAY_PH) == p2_singleton_ph assert get_seconds_and_delayed_puzhash_from_p2_singleton_puzzle( p2_singleton_puz) == (DELAY_TIME, DELAY_PH) coin_db.farm_coin(p2_singleton_ph, time, 1750000000000) coin_sols: List[CoinSolution] = create_absorb_spend( launcher_coinsol, pool_state, launcher_coin, 2, GENESIS_CHALLENGE, DELAY_TIME, DELAY_PH, # height ) # Spend it! coin_db.update_coin_store_for_spend_bundle( SpendBundle(coin_sols, G2Element()), time, DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM, ) # ABSORB A NON EXISTENT REWARD (Negative test) last_coinsol: CoinSolution = list( filter( lambda e: e.coin.amount == START_AMOUNT, coin_sols, ))[0] coin_sols: List[CoinSolution] = create_absorb_spend( last_coinsol, pool_state, launcher_coin, 2, GENESIS_CHALLENGE, DELAY_TIME, DELAY_PH, # height ) # filter for only the singleton solution singleton_coinsol: CoinSolution = list( filter( lambda e: e.coin.amount == START_AMOUNT, coin_sols, ))[0] # Spend it and hope it fails! try: coin_db.update_coin_store_for_spend_bundle( SpendBundle([singleton_coinsol], G2Element()), time, DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM, ) except BadSpendBundleError as e: assert str( e ) == "condition validation failure Err.ASSERT_ANNOUNCE_CONSUMED_FAILED" # SPEND A NON-REWARD P2_SINGLETON (Negative test) # create the dummy coin non_reward_p2_singleton = Coin( bytes32(32 * b"3"), p2_singleton_ph, uint64(1337), ) coin_db._add_coin_entry(non_reward_p2_singleton, time) # construct coin solution for the p2_singleton coin bad_coinsol = CoinSolution( non_reward_p2_singleton, p2_singleton_puz, Program.to([ pooling_innerpuz.get_tree_hash(), non_reward_p2_singleton.name(), ]), ) # Spend it and hope it fails! try: coin_db.update_coin_store_for_spend_bundle( SpendBundle([singleton_coinsol, bad_coinsol], G2Element()), time, DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM, ) except BadSpendBundleError as e: assert str( e ) == "condition validation failure Err.ASSERT_ANNOUNCE_CONSUMED_FAILED" # ENTER WAITING ROOM # find the singleton singleton = get_most_recent_singleton_coin_from_coin_solution( last_coinsol) # get the relevant coin solution travel_coinsol, _ = create_travel_spend( last_coinsol, launcher_coin, pool_state, target_pool_state, GENESIS_CHALLENGE, DELAY_TIME, DELAY_PH, ) # Test that we can retrieve the extra data assert solution_to_extra_data(travel_coinsol) == target_pool_state # sign the serialized state data = Program.to(bytes(target_pool_state)).get_tree_hash() sig: G2Element = AugSchemeMPL.sign( sk, (data + singleton.name() + DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA), ) # Spend it! coin_db.update_coin_store_for_spend_bundle( SpendBundle([travel_coinsol], sig), time, DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM, ) # ESCAPE TOO FAST (Negative test) # find the singleton singleton = get_most_recent_singleton_coin_from_coin_solution( travel_coinsol) # get the relevant coin solution return_coinsol, _ = create_travel_spend( travel_coinsol, launcher_coin, target_pool_state, pool_state, GENESIS_CHALLENGE, DELAY_TIME, DELAY_PH, ) # sign the serialized target state sig = AugSchemeMPL.sign( sk, (data + singleton.name() + DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA), ) # Spend it and hope it fails! try: coin_db.update_coin_store_for_spend_bundle( SpendBundle([return_coinsol], sig), time, DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM, ) except BadSpendBundleError as e: assert str( e ) == "condition validation failure Err.ASSERT_HEIGHT_RELATIVE_FAILED" # ABSORB WHILE IN WAITING ROOM time = CoinTimestamp(10000060, 3) # create the farming reward coin_db.farm_coin(p2_singleton_ph, time, 1750000000000) # generate relevant coin solutions coin_sols: List[CoinSolution] = create_absorb_spend( travel_coinsol, target_pool_state, launcher_coin, 3, GENESIS_CHALLENGE, DELAY_TIME, DELAY_PH, # height ) # Spend it! coin_db.update_coin_store_for_spend_bundle( SpendBundle(coin_sols, G2Element()), time, DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM, ) # LEAVE THE WAITING ROOM time = CoinTimestamp(20000000, 10000) # find the singleton singleton_coinsol: CoinSolution = list( filter( lambda e: e.coin.amount == START_AMOUNT, coin_sols, ))[0] singleton: Coin = get_most_recent_singleton_coin_from_coin_solution( singleton_coinsol) # get the relevant coin solution return_coinsol, _ = create_travel_spend( singleton_coinsol, launcher_coin, target_pool_state, pool_state, GENESIS_CHALLENGE, DELAY_TIME, DELAY_PH, ) # Test that we can retrieve the extra data assert solution_to_extra_data(return_coinsol) == pool_state # sign the serialized target state data = Program.to([ pooling_innerpuz.get_tree_hash(), START_AMOUNT, bytes(pool_state) ]).get_tree_hash() sig: G2Element = AugSchemeMPL.sign( sk, (data + singleton.name() + DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA), ) # Spend it! coin_db.update_coin_store_for_spend_bundle( SpendBundle([return_coinsol], sig), time, DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM, ) # ABSORB ONCE MORE FOR GOOD MEASURE time = CoinTimestamp(20000000, 10005) # create the farming reward coin_db.farm_coin(p2_singleton_ph, time, 1750000000000) coin_sols: List[CoinSolution] = create_absorb_spend( return_coinsol, pool_state, launcher_coin, 10005, GENESIS_CHALLENGE, DELAY_TIME, DELAY_PH, # height ) # Spend it! coin_db.update_coin_store_for_spend_bundle( SpendBundle(coin_sols, G2Element()), time, DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM, )
def create_unfinished_block( constants: ConsensusConstants, sub_slot_start_total_iters: uint128, sub_slot_iters: uint64, signage_point_index: uint8, sp_iters: uint64, ip_iters: uint64, proof_of_space: ProofOfSpace, slot_cc_challenge: bytes32, farmer_reward_puzzle_hash: bytes32, pool_target: PoolTarget, get_plot_signature: Callable[[bytes32, G1Element], G2Element], get_pool_signature: Callable[[PoolTarget, Optional[G1Element]], Optional[G2Element]], signage_point: SignagePoint, timestamp: uint64, blocks: BlockchainInterface, seed: bytes32 = b"", block_generator: Optional[BlockGenerator] = None, aggregate_sig: G2Element = G2Element(), additions: Optional[List[Coin]] = None, removals: Optional[List[Coin]] = None, prev_block: Optional[BlockRecord] = None, finished_sub_slots_input: List[EndOfSubSlotBundle] = None, ) -> UnfinishedBlock: """ Creates a new unfinished block using all the information available at the signage point. This will have to be modified using information from the infusion point. Args: constants: consensus constants being used for this chain sub_slot_start_total_iters: the starting sub-slot iters at the signage point sub-slot sub_slot_iters: sub-slot-iters at the infusion point epoch signage_point_index: signage point index of the block to create sp_iters: sp_iters of the block to create ip_iters: ip_iters of the block to create proof_of_space: proof of space of the block to create slot_cc_challenge: challenge hash at the sp sub-slot farmer_reward_puzzle_hash: where to pay out farmer rewards pool_target: where to pay out pool rewards get_plot_signature: function that returns signature corresponding to plot public key get_pool_signature: function that returns signature corresponding to pool public key signage_point: signage point information (VDFs) timestamp: timestamp to add to the foliage block, if created seed: seed to randomize chain block_generator: transactions to add to the foliage block, if created aggregate_sig: aggregate of all transctions (or infinity element) additions: Coins added in spend_bundle removals: Coins removed in spend_bundle prev_block: previous block (already in chain) from the signage point blocks: dictionary from header hash to SBR of all included SBR finished_sub_slots_input: finished_sub_slots at the signage point Returns: """ if finished_sub_slots_input is None: finished_sub_slots: List[EndOfSubSlotBundle] = [] else: finished_sub_slots = finished_sub_slots_input.copy() overflow: bool = sp_iters > ip_iters total_iters_sp: uint128 = uint128(sub_slot_start_total_iters + sp_iters) is_genesis: bool = prev_block is None new_sub_slot: bool = len(finished_sub_slots) > 0 cc_sp_hash: Optional[bytes32] = slot_cc_challenge # Only enters this if statement if we are in testing mode (making VDF proofs here) if signage_point.cc_vdf is not None: assert signage_point.rc_vdf is not None cc_sp_hash = signage_point.cc_vdf.output.get_hash() rc_sp_hash = signage_point.rc_vdf.output.get_hash() else: if new_sub_slot: rc_sp_hash = finished_sub_slots[-1].reward_chain.get_hash() else: if is_genesis: rc_sp_hash = constants.GENESIS_CHALLENGE else: assert prev_block is not None assert blocks is not None curr = prev_block while not curr.first_in_sub_slot: curr = blocks.block_record(curr.prev_hash) assert curr.finished_reward_slot_hashes is not None rc_sp_hash = curr.finished_reward_slot_hashes[-1] signage_point = SignagePoint(None, None, None, None) cc_sp_signature: Optional[G2Element] = get_plot_signature(cc_sp_hash, proof_of_space.plot_public_key) rc_sp_signature: Optional[G2Element] = get_plot_signature(rc_sp_hash, proof_of_space.plot_public_key) assert cc_sp_signature is not None assert rc_sp_signature is not None assert blspy.AugSchemeMPL.verify(proof_of_space.plot_public_key, cc_sp_hash, cc_sp_signature) total_iters = uint128(sub_slot_start_total_iters + ip_iters + (sub_slot_iters if overflow else 0)) rc_block = RewardChainBlockUnfinished( total_iters, signage_point_index, slot_cc_challenge, proof_of_space, signage_point.cc_vdf, cc_sp_signature, signage_point.rc_vdf, rc_sp_signature, ) if additions is None: additions = [] if removals is None: removals = [] (foliage, foliage_transaction_block, transactions_info,) = create_foliage( constants, rc_block, block_generator, aggregate_sig, additions, removals, prev_block, blocks, total_iters_sp, timestamp, farmer_reward_puzzle_hash, pool_target, get_plot_signature, get_pool_signature, seed, ) return UnfinishedBlock( finished_sub_slots, rc_block, signage_point.cc_proof, signage_point.rc_proof, foliage, foliage_transaction_block, transactions_info, block_generator.program if block_generator else None, block_generator.block_height_list() if block_generator else [], )
async def test_wallet_rpc(self, two_wallet_nodes, trusted): test_rpc_port = uint16(21529) test_rpc_port_2 = uint16(21536) test_rpc_port_node = uint16(21530) num_blocks = 5 full_nodes, wallets = two_wallet_nodes full_node_api = full_nodes[0] full_node_server = full_node_api.full_node.server wallet_node, server_2 = wallets[0] wallet_node_2, server_3 = wallets[1] wallet = wallet_node.wallet_state_manager.main_wallet wallet_2 = wallet_node_2.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() ph_2 = await wallet_2.get_new_puzzlehash() await server_2.start_client(PeerInfo("localhost", uint16(full_node_server._port)), None) await server_3.start_client(PeerInfo("localhost", uint16(full_node_server._port)), None) if trusted: wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()} wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()} else: wallet_node.config["trusted_peers"] = {} wallet_node_2.config["trusted_peers"] = {} for i in range(0, num_blocks): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph)) initial_funds = sum( [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)] ) initial_funds_eventually = sum( [ calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1) ] ) wallet_rpc_api = WalletRpcApi(wallet_node) wallet_rpc_api_2 = WalletRpcApi(wallet_node_2) config = bt.config hostname = config["self_hostname"] daemon_port = config["daemon_port"] def stop_node_cb(): pass full_node_rpc_api = FullNodeRpcApi(full_node_api.full_node) rpc_cleanup_node = await start_rpc_server( full_node_rpc_api, hostname, daemon_port, test_rpc_port_node, stop_node_cb, bt.root_path, config, connect_to_daemon=False, ) rpc_cleanup = await start_rpc_server( wallet_rpc_api, hostname, daemon_port, test_rpc_port, stop_node_cb, bt.root_path, config, connect_to_daemon=False, ) rpc_cleanup_2 = await start_rpc_server( wallet_rpc_api_2, hostname, daemon_port, test_rpc_port_2, stop_node_cb, bt.root_path, config, connect_to_daemon=False, ) await time_out_assert(5, wallet.get_confirmed_balance, initial_funds) await time_out_assert(5, wallet.get_unconfirmed_balance, initial_funds) client = await WalletRpcClient.create(self_hostname, test_rpc_port, bt.root_path, config) client_2 = await WalletRpcClient.create(self_hostname, test_rpc_port_2, bt.root_path, config) client_node = await FullNodeRpcClient.create(self_hostname, test_rpc_port_node, bt.root_path, config) try: await time_out_assert(5, client.get_synced) addr = encode_puzzle_hash(await wallet_node_2.wallet_state_manager.main_wallet.get_new_puzzlehash(), "xch") tx_amount = 15600000 try: await client.send_transaction("1", 100000000000000001, addr) raise Exception("Should not create high value tx") except ValueError: pass # Tests sending a basic transaction tx = await client.send_transaction("1", tx_amount, addr, memos=["this is a basic tx"]) transaction_id = tx.name async def tx_in_mempool(): tx = await client.get_transaction("1", transaction_id) return tx.is_in_mempool() await time_out_assert(5, tx_in_mempool, True) await time_out_assert(5, wallet.get_unconfirmed_balance, initial_funds - tx_amount) assert (await client.get_wallet_balance("1"))["unconfirmed_wallet_balance"] == initial_funds - tx_amount assert (await client.get_wallet_balance("1"))["confirmed_wallet_balance"] == initial_funds for i in range(0, 5): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph_2)) async def eventual_balance(): return (await client.get_wallet_balance("1"))["confirmed_wallet_balance"] async def eventual_balance_det(c, wallet_id: str): return (await c.get_wallet_balance(wallet_id))["confirmed_wallet_balance"] # Checks that the memo can be retrieved tx_confirmed = await client.get_transaction("1", transaction_id) assert tx_confirmed.confirmed assert len(tx_confirmed.get_memos()) == 1 assert [b"this is a basic tx"] in tx_confirmed.get_memos().values() assert list(tx_confirmed.get_memos().keys())[0] in [a.name() for a in tx.spend_bundle.additions()] await time_out_assert(5, eventual_balance, initial_funds_eventually - tx_amount) # Tests offline signing ph_3 = await wallet_node_2.wallet_state_manager.main_wallet.get_new_puzzlehash() ph_4 = await wallet_node_2.wallet_state_manager.main_wallet.get_new_puzzlehash() ph_5 = await wallet_node_2.wallet_state_manager.main_wallet.get_new_puzzlehash() # Test basic transaction to one output and coin announcement signed_tx_amount = 888000 tx_coin_announcements = [ Announcement( std_hash(b"coin_id_1"), std_hash(b"message"), b"\xca", ), Announcement( std_hash(b"coin_id_2"), bytes(Program.to("a string")), ), ] tx_res: TransactionRecord = await client.create_signed_transaction( [{"amount": signed_tx_amount, "puzzle_hash": ph_3}], coin_announcements=tx_coin_announcements ) assert tx_res.fee_amount == 0 assert tx_res.amount == signed_tx_amount assert len(tx_res.additions) == 2 # The output and the change assert any([addition.amount == signed_tx_amount for addition in tx_res.additions]) # check error for a ASSERT_ANNOUNCE_CONSUMED_FAILED and if the error is not there throw a value error try: push_res = await client_node.push_tx(tx_res.spend_bundle) except ValueError as error: error_string = error.args[0]["error"] # noqa: # pylint: disable=E1126 if error_string.find("ASSERT_ANNOUNCE_CONSUMED_FAILED") == -1: raise ValueError from error # # Test basic transaction to one output and puzzle announcement signed_tx_amount = 888000 tx_puzzle_announcements = [ Announcement( std_hash(b"puzzle_hash_1"), b"message", b"\xca", ), Announcement( std_hash(b"puzzle_hash_2"), bytes(Program.to("a string")), ), ] tx_res: TransactionRecord = await client.create_signed_transaction( [{"amount": signed_tx_amount, "puzzle_hash": ph_3}], puzzle_announcements=tx_puzzle_announcements ) assert tx_res.fee_amount == 0 assert tx_res.amount == signed_tx_amount assert len(tx_res.additions) == 2 # The output and the change assert any([addition.amount == signed_tx_amount for addition in tx_res.additions]) # check error for a ASSERT_ANNOUNCE_CONSUMED_FAILED and if the error is not there throw a value error try: push_res = await client_node.push_tx(tx_res.spend_bundle) except ValueError as error: error_string = error.args[0]["error"] # noqa: # pylint: disable=E1126 if error_string.find("ASSERT_ANNOUNCE_CONSUMED_FAILED") == -1: raise ValueError from error # Test basic transaction to one output signed_tx_amount = 888000 tx_res: TransactionRecord = await client.create_signed_transaction( [{"amount": signed_tx_amount, "puzzle_hash": ph_3, "memos": ["My memo"]}] ) assert tx_res.fee_amount == 0 assert tx_res.amount == signed_tx_amount assert len(tx_res.additions) == 2 # The output and the change assert any([addition.amount == signed_tx_amount for addition in tx_res.additions]) push_res = await client.push_tx(tx_res.spend_bundle) assert push_res["success"] assert (await client.get_wallet_balance("1"))[ "confirmed_wallet_balance" ] == initial_funds_eventually - tx_amount for i in range(0, 5): await client.farm_block(encode_puzzle_hash(ph_2, "xch")) await asyncio.sleep(0.5) await time_out_assert(5, eventual_balance, initial_funds_eventually - tx_amount - signed_tx_amount) # Test transaction to two outputs, from a specified coin, with a fee coin_to_spend = None for addition in tx_res.additions: if addition.amount != signed_tx_amount: coin_to_spend = addition assert coin_to_spend is not None tx_res = await client.create_signed_transaction( [{"amount": 444, "puzzle_hash": ph_4, "memos": ["hhh"]}, {"amount": 999, "puzzle_hash": ph_5}], coins=[coin_to_spend], fee=100, ) assert tx_res.fee_amount == 100 assert tx_res.amount == 444 + 999 assert len(tx_res.additions) == 3 # The outputs and the change assert any([addition.amount == 444 for addition in tx_res.additions]) assert any([addition.amount == 999 for addition in tx_res.additions]) assert sum([rem.amount for rem in tx_res.removals]) - sum([ad.amount for ad in tx_res.additions]) == 100 push_res = await client_node.push_tx(tx_res.spend_bundle) assert push_res["success"] for i in range(0, 5): await client.farm_block(encode_puzzle_hash(ph_2, "xch")) await asyncio.sleep(0.5) found: bool = False for addition in tx_res.spend_bundle.additions(): if addition.amount == 444: cr: Optional[CoinRecord] = await client_node.get_coin_record_by_name(addition.name()) assert cr is not None spend: CoinSpend = await client_node.get_puzzle_and_solution( addition.parent_coin_info, cr.confirmed_block_index ) sb: SpendBundle = SpendBundle([spend], G2Element()) assert compute_memos(sb) == {addition.name(): [b"hhh"]} found = True assert found new_balance = initial_funds_eventually - tx_amount - signed_tx_amount - 444 - 999 - 100 await time_out_assert(5, eventual_balance, new_balance) send_tx_res: TransactionRecord = await client.send_transaction_multi( "1", [ {"amount": 555, "puzzle_hash": ph_4, "memos": ["FiMemo"]}, {"amount": 666, "puzzle_hash": ph_5, "memos": ["SeMemo"]}, ], fee=200, ) assert send_tx_res is not None assert send_tx_res.fee_amount == 200 assert send_tx_res.amount == 555 + 666 assert len(send_tx_res.additions) == 3 # The outputs and the change assert any([addition.amount == 555 for addition in send_tx_res.additions]) assert any([addition.amount == 666 for addition in send_tx_res.additions]) assert ( sum([rem.amount for rem in send_tx_res.removals]) - sum([ad.amount for ad in send_tx_res.additions]) == 200 ) await asyncio.sleep(3) for i in range(0, 5): await client.farm_block(encode_puzzle_hash(ph_2, "xch")) await asyncio.sleep(0.5) new_balance = new_balance - 555 - 666 - 200 await time_out_assert(5, eventual_balance, new_balance) address = await client.get_next_address("1", True) assert len(address) > 10 transactions = await client.get_transactions("1") assert len(transactions) > 1 all_transactions = await client.get_transactions("1") # Test transaction pagination some_transactions = await client.get_transactions("1", 0, 5) some_transactions_2 = await client.get_transactions("1", 5, 10) assert some_transactions == all_transactions[0:5] assert some_transactions_2 == all_transactions[5:10] # Testing sorts # Test the default sort (CONFIRMED_AT_HEIGHT) assert all_transactions == sorted(all_transactions, key=attrgetter("confirmed_at_height")) all_transactions = await client.get_transactions("1", reverse=True) assert all_transactions == sorted(all_transactions, key=attrgetter("confirmed_at_height"), reverse=True) # Test RELEVANCE await client.send_transaction("1", 1, encode_puzzle_hash(ph_2, "xch")) # Create a pending tx all_transactions = await client.get_transactions("1", sort_key=SortKey.RELEVANCE) sorted_transactions = sorted(all_transactions, key=attrgetter("created_at_time"), reverse=True) sorted_transactions = sorted(sorted_transactions, key=attrgetter("confirmed_at_height"), reverse=True) sorted_transactions = sorted(sorted_transactions, key=attrgetter("confirmed")) assert all_transactions == sorted_transactions all_transactions = await client.get_transactions("1", sort_key=SortKey.RELEVANCE, reverse=True) sorted_transactions = sorted(all_transactions, key=attrgetter("created_at_time")) sorted_transactions = sorted(sorted_transactions, key=attrgetter("confirmed_at_height")) sorted_transactions = sorted(sorted_transactions, key=attrgetter("confirmed"), reverse=True) assert all_transactions == sorted_transactions # Checks that the memo can be retrieved tx_confirmed = await client.get_transaction("1", send_tx_res.name) assert tx_confirmed.confirmed if isinstance(tx_confirmed, SpendBundle): memos = compute_memos(tx_confirmed) else: memos = tx_confirmed.get_memos() assert len(memos) == 2 print(memos) assert [b"FiMemo"] in memos.values() assert [b"SeMemo"] in memos.values() assert list(memos.keys())[0] in [a.name() for a in send_tx_res.spend_bundle.additions()] assert list(memos.keys())[1] in [a.name() for a in send_tx_res.spend_bundle.additions()] ############## # CATS # ############## # Creates a wallet and a CAT with 20 mojos res = await client.create_new_cat_and_wallet(20) assert res["success"] cat_0_id = res["wallet_id"] asset_id = bytes.fromhex(res["asset_id"]) assert len(asset_id) > 0 bal_0 = await client.get_wallet_balance(cat_0_id) assert bal_0["confirmed_wallet_balance"] == 0 assert bal_0["pending_coin_removal_count"] == 1 col = await client.get_cat_asset_id(cat_0_id) assert col == asset_id assert (await client.get_cat_name(cat_0_id)) == "CAT Wallet" await client.set_cat_name(cat_0_id, "My cat") assert (await client.get_cat_name(cat_0_id)) == "My cat" wid, name = await client.cat_asset_id_to_name(col) assert wid == cat_0_id assert name == "My cat" should_be_none = await client.cat_asset_id_to_name(bytes([0] * 32)) assert should_be_none is None verified_asset_id = next(iter(DEFAULT_CATS.items()))[1]["asset_id"] should_be_none, name = await client.cat_asset_id_to_name(bytes.fromhex(verified_asset_id)) assert should_be_none is None assert name == next(iter(DEFAULT_CATS.items()))[1]["name"] await asyncio.sleep(1) for i in range(0, 5): await client.farm_block(encode_puzzle_hash(ph_2, "xch")) await asyncio.sleep(0.5) await time_out_assert(10, eventual_balance_det, 20, client, cat_0_id) bal_0 = await client.get_wallet_balance(cat_0_id) assert bal_0["pending_coin_removal_count"] == 0 assert bal_0["unspent_coin_count"] == 1 # Creates a second wallet with the same CAT res = await client_2.create_wallet_for_existing_cat(asset_id) assert res["success"] cat_1_id = res["wallet_id"] colour_1 = bytes.fromhex(res["asset_id"]) assert colour_1 == asset_id await asyncio.sleep(1) for i in range(0, 5): await client.farm_block(encode_puzzle_hash(ph_2, "xch")) await asyncio.sleep(0.5) bal_1 = await client_2.get_wallet_balance(cat_1_id) assert bal_1["confirmed_wallet_balance"] == 0 addr_0 = await client.get_next_address(cat_0_id, False) addr_1 = await client_2.get_next_address(cat_1_id, False) assert addr_0 != addr_1 await client.cat_spend(cat_0_id, 4, addr_1, 0, ["the cat memo"]) await asyncio.sleep(1) for i in range(0, 5): await client.farm_block(encode_puzzle_hash(ph_2, "xch")) await asyncio.sleep(0.5) await time_out_assert(10, eventual_balance_det, 16, client, cat_0_id) await time_out_assert(10, eventual_balance_det, 4, client_2, cat_1_id) ########## # Offers # ########## # Create an offer of 5 chia for one CAT offer, trade_record = await client.create_offer_for_ids({uint32(1): -5, cat_0_id: 1}, validate_only=True) all_offers = await client.get_all_offers() assert len(all_offers) == 0 assert offer is None offer, trade_record = await client.create_offer_for_ids({uint32(1): -5, cat_0_id: 1}, fee=uint64(1)) summary = await client.get_offer_summary(offer) assert summary == {"offered": {"xch": 5}, "requested": {col.hex(): 1}} assert await client.check_offer_validity(offer) all_offers = await client.get_all_offers(file_contents=True) assert len(all_offers) == 1 assert TradeStatus(all_offers[0].status) == TradeStatus.PENDING_ACCEPT assert all_offers[0].offer == bytes(offer) trade_record = await client_2.take_offer(offer, fee=uint64(1)) assert TradeStatus(trade_record.status) == TradeStatus.PENDING_CONFIRM await client.cancel_offer(offer.name(), secure=False) trade_record = await client.get_offer(offer.name(), file_contents=True) assert trade_record.offer == bytes(offer) assert TradeStatus(trade_record.status) == TradeStatus.CANCELLED await client.cancel_offer(offer.name(), fee=uint64(1), secure=True) trade_record = await client.get_offer(offer.name()) assert TradeStatus(trade_record.status) == TradeStatus.PENDING_CANCEL new_offer, new_trade_record = await client.create_offer_for_ids({uint32(1): -5, cat_0_id: 1}, fee=uint64(1)) all_offers = await client.get_all_offers() assert len(all_offers) == 2 await asyncio.sleep(1) for i in range(0, 5): await client.farm_block(encode_puzzle_hash(ph_2, "xch")) await asyncio.sleep(0.5) async def is_trade_confirmed(client, trade) -> bool: trade_record = await client.get_offer(trade.name()) return TradeStatus(trade_record.status) == TradeStatus.CONFIRMED time_out_assert(15, is_trade_confirmed, True, client, offer) # Test trade sorting def only_ids(trades): return [t.trade_id for t in trades] trade_record = await client.get_offer(offer.name()) all_offers = await client.get_all_offers(include_completed=True) # confirmed at index descending assert len(all_offers) == 2 assert only_ids(all_offers) == only_ids([trade_record, new_trade_record]) all_offers = await client.get_all_offers( include_completed=True, reverse=True ) # confirmed at index ascending assert only_ids(all_offers) == only_ids([new_trade_record, trade_record]) all_offers = await client.get_all_offers(include_completed=True, sort_key="RELEVANCE") # most relevant assert only_ids(all_offers) == only_ids([new_trade_record, trade_record]) all_offers = await client.get_all_offers( include_completed=True, sort_key="RELEVANCE", reverse=True ) # least relevant assert only_ids(all_offers) == only_ids([trade_record, new_trade_record]) # Test pagination all_offers = await client.get_all_offers(include_completed=True, start=0, end=1) assert len(all_offers) == 1 all_offers = await client.get_all_offers(include_completed=True, start=50) assert len(all_offers) == 0 all_offers = await client.get_all_offers(include_completed=True, start=0, end=50) assert len(all_offers) == 2 # Keys and addresses address = await client.get_next_address("1", True) assert len(address) > 10 all_transactions = await client.get_transactions("1") some_transactions = await client.get_transactions("1", 0, 5) some_transactions_2 = await client.get_transactions("1", 5, 10) assert len(all_transactions) > 1 assert some_transactions == all_transactions[0:5] assert some_transactions_2 == all_transactions[5:10] transaction_count = await client.get_transaction_count("1") assert transaction_count == len(all_transactions) pks = await client.get_public_keys() assert len(pks) == 1 assert (await client.get_height_info()) > 0 created_tx = await client.send_transaction("1", tx_amount, addr) async def tx_in_mempool_2(): tx = await client.get_transaction("1", created_tx.name) return tx.is_in_mempool() await time_out_assert(5, tx_in_mempool_2, True) assert len(await wallet.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(1)) == 1 await client.delete_unconfirmed_transactions("1") assert len(await wallet.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(1)) == 0 sk_dict = await client.get_private_key(pks[0]) assert sk_dict["fingerprint"] == pks[0] assert sk_dict["sk"] is not None assert sk_dict["pk"] is not None assert sk_dict["seed"] is not None mnemonic = await client.generate_mnemonic() assert len(mnemonic) == 24 await client.add_key(mnemonic) pks = await client.get_public_keys() assert len(pks) == 2 await client.log_in_and_skip(pks[1]) sk_dict = await client.get_private_key(pks[1]) assert sk_dict["fingerprint"] == pks[1] # Add in reward addresses into farmer and pool for testing delete key checks # set farmer to first private key sk = await wallet_node.get_key_for_fingerprint(pks[0]) test_ph = create_puzzlehash_for_pk(master_sk_to_wallet_sk(sk, uint32(0)).get_g1()) test_config = load_config(wallet_node.root_path, "config.yaml") test_config["farmer"]["xch_target_address"] = encode_puzzle_hash(test_ph, "txch") # set pool to second private key sk = await wallet_node.get_key_for_fingerprint(pks[1]) test_ph = create_puzzlehash_for_pk(master_sk_to_wallet_sk(sk, uint32(0)).get_g1()) test_config["pool"]["xch_target_address"] = encode_puzzle_hash(test_ph, "txch") save_config(wallet_node.root_path, "config.yaml", test_config) # Check first key sk_dict = await client.check_delete_key(pks[0]) assert sk_dict["fingerprint"] == pks[0] assert sk_dict["used_for_farmer_rewards"] is True assert sk_dict["used_for_pool_rewards"] is False # Check second key sk_dict = await client.check_delete_key(pks[1]) assert sk_dict["fingerprint"] == pks[1] assert sk_dict["used_for_farmer_rewards"] is False assert sk_dict["used_for_pool_rewards"] is True # Check unknown key sk_dict = await client.check_delete_key(123456) assert sk_dict["fingerprint"] == 123456 assert sk_dict["used_for_farmer_rewards"] is False assert sk_dict["used_for_pool_rewards"] is False await client.delete_key(pks[0]) await client.log_in_and_skip(pks[1]) assert len(await client.get_public_keys()) == 1 assert not (await client.get_sync_status()) wallets = await client.get_wallets() assert len(wallets) == 1 balance = await client.get_wallet_balance(wallets[0]["id"]) assert balance["unconfirmed_wallet_balance"] == 0 try: await client.send_transaction(wallets[0]["id"], 100, addr) raise Exception("Should not create tx if no balance") except ValueError: pass # Delete all keys await client.delete_all_keys() assert len(await client.get_public_keys()) == 0 finally: # Checks that the RPC manages to stop the node client.close() client_2.close() client_node.close() await client.await_closed() await client_2.await_closed() await client_node.await_closed() await rpc_cleanup() await rpc_cleanup_2() await rpc_cleanup_node()