async def get_next_address(self, request: Dict) -> Dict: """ Returns a new address """ assert self.service.wallet_state_manager is not None if request["new_address"] is True: create_new = True else: create_new = False wallet_id = uint32(int(request["wallet_id"])) wallet = self.service.wallet_state_manager.wallets[wallet_id] selected = self.service.config["selected_network"] prefix = self.service.config["network_overrides"]["config"][selected][ "address_prefix"] if wallet.type() == WalletType.STANDARD_WALLET: raw_puzzle_hash = await wallet.get_puzzle_hash(create_new) address = encode_puzzle_hash(raw_puzzle_hash, prefix) elif wallet.type() == WalletType.COLOURED_COIN: raw_puzzle_hash = await wallet.get_puzzle_hash(create_new) address = encode_puzzle_hash(raw_puzzle_hash, prefix) else: raise ValueError( f"Wallet type {wallet.type()} cannot create puzzle hashes") return { "wallet_id": wallet_id, "address": address, }
async def setup_farmer( port, consensus_constants: ConsensusConstants, b_tools, full_node_port: Optional[uint16] = None, ): config = bt.config["farmer"] config_pool = bt.config["pool"] config["xch_target_address"] = encode_puzzle_hash(b_tools.farmer_ph, "xch") config["pool_public_keys"] = [bytes(pk).hex() for pk in b_tools.pool_pubkeys] config["port"] = port config_pool["xch_target_address"] = encode_puzzle_hash(b_tools.pool_ph, "xch") if full_node_port: config["full_node_peer"]["host"] = self_hostname config["full_node_peer"]["port"] = full_node_port else: del config["full_node_peer"] kwargs = service_kwargs_for_farmer(b_tools.root_path, config, config_pool, b_tools.keychain, consensus_constants) kwargs.update( parse_cli_args=False, connect_to_daemon=False, ) service = Service(**kwargs) await service.start() yield service._api, service._node.server service.stop() await service.wait_closed()
def show_all_keys(): """ Prints all keys and mnemonics (if available). """ root_path = DEFAULT_ROOT_PATH config = load_config(root_path, "config.yaml") private_keys = keychain.get_all_private_keys() selected = config["selected_network"] prefix = config["network_overrides"]["config"][selected]["address_prefix"] if len(private_keys) == 0: print("There are no saved private keys") return print("Showing all private keys:") for sk, seed in private_keys: print("") print("Fingerprint:", sk.get_g1().get_fingerprint()) print("Master public key (m):", sk.get_g1()) print("Master private key (m):", bytes(sk).hex()) print( "Farmer public key (m/12381/8444/0/0)::", master_sk_to_farmer_sk(sk).get_g1(), ) print("Pool public key (m/12381/8444/1/0):", master_sk_to_pool_sk(sk).get_g1()) print( "First wallet key (m/12381/8444/2/0):", master_sk_to_wallet_sk(sk, uint32(0)).get_g1(), ) print( "First wallet address:", encode_puzzle_hash(create_puzzlehash_for_pk(master_sk_to_wallet_sk(sk, uint32(0)).get_g1()), prefix), ) assert seed is not None mnemonic = bytes_to_mnemonic(seed) print(" Mnemonic seed (24 secret words):") print(mnemonic)
async def get_transactions(self, request: Dict) -> Dict: assert self.service.wallet_state_manager is not None wallet_id = int(request["wallet_id"]) if "start" in request: start = request["start"] else: start = 0 if "end" in request: end = request["end"] else: end = 50 transactions = await self.service.wallet_state_manager.tx_store.get_transactions_between( wallet_id, start, end) formatted_transactions = [] selected = self.service.config["selected_network"] prefix = self.service.config["network_overrides"]["config"][selected][ "address_prefix"] for tx in transactions: formatted = tx.to_json_dict() formatted["to_address"] = encode_puzzle_hash( tx.to_puzzle_hash, prefix) formatted_transactions.append(formatted) return { "transactions": formatted_transactions, "wallet_id": wallet_id, }
def puzzHash(pk): child_sk: PrivateKey = PrivateKey.from_bytes(bytes.fromhex(pk)) child_public_key = child_sk.get_g1() puzzle = puzzle_for_pk(child_public_key) puzzle_hash = puzzle.get_tree_hash() # xch address = encode_puzzle_hash(puzzle_hash, "txch") return address
def show_all_keys(show_mnemonic: bool): """ Prints all keys and mnemonics (if available). """ root_path = DEFAULT_ROOT_PATH config = load_config(root_path, "config.yaml") private_keys = keychain.get_all_private_keys() selected = config["selected_network"] prefix = config["network_overrides"]["config"][selected]["address_prefix"] if len(private_keys) == 0: print("There are no saved private keys") return None msg = "Showing all public keys derived from your private keys:" if show_mnemonic: msg = "Showing all public and private keys" print(msg) result_list = [] for sk, seed in private_keys: # print("") # print("Fingerprint:", sk.get_g1().get_fingerprint()) # print("Master public key (m):", sk.get_g1()) # print( # "Farmer public key (m/12381/8444/0/0):", # master_sk_to_farmer_sk(sk).get_g1(), # ) # print("Pool public key (m/12381/8444/1/0):", master_sk_to_pool_sk(sk).get_g1()) # print( # "First wallet address:", # encode_puzzle_hash(create_puzzlehash_for_pk(master_sk_to_wallet_sk(sk, uint32(0)).get_g1()), prefix), # ) # assert seed is not None # print("Master private key (m):", bytes(sk).hex()) # print( # "First wallet secret key (m/12381/8444/2/0):", # master_sk_to_wallet_sk(sk, uint32(0)).get_g1(), # ) mnemonic = bytes_to_mnemonic(seed) # print(" Mnemonic seed (24 secret words):") # print(mnemonic) data = { "fingerprint": sk.get_g1().get_fingerprint(), "mnemonic": ','.join(mnemonic.split()), "address": encode_puzzle_hash( create_puzzlehash_for_pk( master_sk_to_wallet_sk(sk, uint32(0)).get_g1()), prefix), "master": sk.get_g1(), "farmer": master_sk_to_farmer_sk(sk).get_g1(), "pool": master_sk_to_pool_sk(sk).get_g1(), } result_list.append(data) return result_list
def mnemonic(): # mnemonic = "imitate obvious arch square fan bike thumb hedgehog crystal innocent shoe glare share father romance local size gloom hurt maid denial weapon wave bulb" mnemonic = "turn acquire ring mind empower ahead section often habit sick sail mountain pen repair catch drum insect file dry trend venue junk novel laptop" seed = mnemonic_to_seed(mnemonic, "") master_private_key = AugSchemeMPL.key_gen(seed) child_private_key = master_sk_to_wallet_sk(master_private_key, 0) child_public_key = child_private_key.get_g1() puzzle = puzzle_for_pk(child_public_key) puzzle_hash = puzzle.get_tree_hash() address = encode_puzzle_hash(puzzle_hash, "txch")
def addr(sk): b = bytes.fromhex(sk) master_private_key = PrivateKey.from_bytes(b) child_public_key = master_private_key.get_g1() puzzle = puzzle_for_pk(child_public_key) puzzle_hash = puzzle.get_tree_hash() # xch address = encode_puzzle_hash(puzzle_hash, "txch") # print(address) return "{\"address\":\"" + address + "\", \"pubkey\":\"" + str( puzzle_hash) + "\"}"
def print_transaction(tx: TransactionRecord, verbose: bool, name) -> None: if verbose: print(tx) else: chia_amount = Decimal(int(tx.amount)) / units["chia"] to_address = encode_puzzle_hash(tx.to_puzzle_hash, name) print(f"Transaction {tx.name}") print(f"Status: {'Confirmed' if tx.confirmed else ('In mempool' if tx.is_in_mempool() else 'Pending')}") print(f"Amount {'sent' if tx.sent else 'received'}: {chia_amount} {name}") print(f"To address: {to_address}") print("Created at:", datetime.fromtimestamp(tx.created_at_time).strftime("%Y-%m-%d %H:%M:%S")) print("")
def to_json_dict_convenience(self, config: Dict) -> Dict: selected = config["selected_network"] prefix = config["network_overrides"]["config"][selected][ "address_prefix"] formatted = self.to_json_dict() formatted["to_address"] = encode_puzzle_hash(self.to_puzzle_hash, prefix) formatted["memos"] = { coin_id.hex(): memo.hex() for coin_id, memos in self.get_memos().items() for memo in memos if memo is not None } return formatted
async def test_farmer_reward_target_endpoints(environment): ( farmer_service, farmer_rpc_api, farmer_rpc_client, harvester_service, harvester_rpc_api, harvester_rpc_client, ) = environment farmer_api = farmer_service._api targets_1 = await farmer_rpc_client.get_reward_targets(False) assert "have_pool_sk" not in targets_1 assert "have_farmer_sk" not in targets_1 targets_2 = await farmer_rpc_client.get_reward_targets(True) assert targets_2["have_pool_sk"] and targets_2["have_farmer_sk"] new_ph: bytes32 = create_puzzlehash_for_pk( master_sk_to_wallet_sk(bt.farmer_master_sk, uint32(10)).get_g1()) new_ph_2: bytes32 = create_puzzlehash_for_pk( master_sk_to_wallet_sk(bt.pool_master_sk, uint32(472)).get_g1()) await farmer_rpc_client.set_reward_targets( encode_puzzle_hash(new_ph, "xch"), encode_puzzle_hash(new_ph_2, "xch")) targets_3 = await farmer_rpc_client.get_reward_targets(True) assert decode_puzzle_hash(targets_3["farmer_target"]) == new_ph assert decode_puzzle_hash(targets_3["pool_target"]) == new_ph_2 assert targets_3["have_pool_sk"] and targets_3["have_farmer_sk"] new_ph_3: bytes32 = create_puzzlehash_for_pk( master_sk_to_wallet_sk(bt.pool_master_sk, uint32(1888)).get_g1()) await farmer_rpc_client.set_reward_targets( None, encode_puzzle_hash(new_ph_3, "xch")) targets_4 = await farmer_rpc_client.get_reward_targets(True) assert decode_puzzle_hash(targets_4["farmer_target"]) == new_ph assert decode_puzzle_hash(targets_4["pool_target"]) == new_ph_3 assert not targets_4["have_pool_sk"] and targets_3["have_farmer_sk"] root_path = farmer_api.farmer._root_path config = load_config(root_path, "config.yaml") assert config["farmer"]["xch_target_address"] == encode_puzzle_hash( new_ph, "xch") assert config["pool"]["xch_target_address"] == encode_puzzle_hash( new_ph_3, "xch") new_ph_3_encoded = encode_puzzle_hash(new_ph_3, "xch") added_char = new_ph_3_encoded + "a" with pytest.raises(ValueError): await farmer_rpc_client.set_reward_targets(None, added_char) replaced_char = new_ph_3_encoded[0:-1] + "a" with pytest.raises(ValueError): await farmer_rpc_client.set_reward_targets(None, replaced_char)
async def create_pool_plot(self, p2_singleton_puzzle_hash: bytes32, shuil=None) -> bytes32: plot_dir = get_plot_dir() temp_dir = get_plot_tmp_dir() args = Namespace() args.size = 22 args.num = 1 args.buffer = 100 args.tmp_dir = temp_dir args.tmp2_dir = plot_dir args.final_dir = plot_dir args.plotid = None args.memo = None args.buckets = 0 args.stripe_size = 2000 args.num_threads = 0 args.nobitfield = False args.exclude_final_dir = False args.list_duplicates = False test_private_keys = [AugSchemeMPL.key_gen(std_hash(b"test_pool_rpc"))] plot_public_key = ProofOfSpace.generate_plot_public_key( master_sk_to_local_sk(test_private_keys[0]).get_g1(), bt.farmer_pk, True) plot_id = ProofOfSpace.calculate_plot_id_ph(p2_singleton_puzzle_hash, plot_public_key) try: plot_keys = PlotKeys( bt.farmer_pk, None, encode_puzzle_hash(p2_singleton_puzzle_hash, "txch")) await create_plots( args, plot_keys, bt.root_path, use_datetime=False, test_private_keys=test_private_keys, ) except KeyboardInterrupt: shuil.rmtree(plot_dir, ignore_errors=True) raise await bt.setup_plots() return plot_id
def derive_wallet_address( root_path: Path, private_key: PrivateKey, index: int, count: int, prefix: Optional[str], non_observer_derivation: bool, show_hd_path: bool, ): """ Generate wallet addresses using keys derived from the provided private key. """ if prefix is None: config: Dict = load_config(root_path, "config.yaml") selected: str = config["selected_network"] prefix = config["network_overrides"]["config"][selected]["address_prefix"] path_indices: List[int] = [12381, 8444, 2] wallet_hd_path_root: str = "m/" for i in path_indices: wallet_hd_path_root += f"{i}{'n' if non_observer_derivation else ''}/" for i in range(index, index + count): if non_observer_derivation: sk = master_sk_to_wallet_sk(private_key, uint32(i)) else: sk = master_sk_to_wallet_sk_unhardened(private_key, uint32(i)) # Generate a wallet address using the standard p2_delegated_puzzle_or_hidden_puzzle puzzle # TODO: consider generating addresses using other puzzles address = encode_puzzle_hash(create_puzzlehash_for_pk(sk.get_g1()), prefix) if show_hd_path: print( f"Wallet address {i} " f"({wallet_hd_path_root + str(i) + ('n' if non_observer_derivation else '')}): {address}" ) else: print(f"Wallet address {i}: {address}")
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()
def _search_derived( current_sk: PrivateKey, search_terms: Tuple[str, ...], path: str, path_indices: Optional[List[int]], limit: int, non_observer_derivation: bool, show_progress: bool, search_public_key: bool, search_private_key: bool, search_address: bool, ) -> List[str]: # Return a subset of search_terms that were found """ Performs a shallow search of keys derived from the current sk for items matching the provided search terms. """ from chia.wallet.derive_keys import _derive_path, _derive_path_unhardened class DerivedSearchResultType(Enum): PUBLIC_KEY = "public key" PRIVATE_KEY = "private key" WALLET_ADDRESS = "wallet address" remaining_search_terms: Dict[str, None] = dict.fromkeys(search_terms) current_path: str = path current_path_indices: List[int] = path_indices if path_indices is not None else [] found_search_terms: List[str] = [] for index in range(limit): found_items: List[Tuple[str, str, DerivedSearchResultType]] = [] printed_match: bool = False current_index_str = str(index) + ("n" if non_observer_derivation else "") current_path += f"{current_index_str}" current_path_indices.append(index) if show_progress: # Output just the current index e.g. "25" or "25n" sys.stdout.write(f"{current_index_str}") sys.stdout.flush() # Derive the private key if non_observer_derivation: child_sk = _derive_path(current_sk, current_path_indices) else: child_sk = _derive_path_unhardened(current_sk, current_path_indices) child_pk: Optional[G1Element] = None # Public key is needed for searching against wallet addresses or public keys if search_public_key or search_address: child_pk = child_sk.get_g1() address: Optional[str] = None if search_address: # Generate a wallet address using the standard p2_delegated_puzzle_or_hidden_puzzle puzzle # TODO: consider generating addresses using other puzzles address = encode_puzzle_hash(create_puzzlehash_for_pk(child_pk), "xch") for term in remaining_search_terms: found_item: Any = None found_item_type: Optional[DerivedSearchResultType] = None if search_private_key and term in str(child_sk): found_item = private_key_string_repr(child_sk) found_item_type = DerivedSearchResultType.PRIVATE_KEY elif search_public_key and child_pk is not None and term in str(child_pk): found_item = child_pk found_item_type = DerivedSearchResultType.PUBLIC_KEY elif search_address and address is not None and term in address: found_item = address found_item_type = DerivedSearchResultType.WALLET_ADDRESS if found_item is not None and found_item_type is not None: found_items.append((term, found_item, found_item_type)) if len(found_items) > 0 and show_progress: print() for (term, found_item, found_item_type) in found_items: # Update remaining_search_terms and found_search_terms del remaining_search_terms[term] found_search_terms.append(term) print( f"Found {found_item_type.value}: {found_item} (HD path: {current_path})" ) # lgtm [py/clear-text-logging-sensitive-data] printed_match = True if len(remaining_search_terms) == 0: break # Remove the last index from the path current_path = current_path[: -len(str(current_index_str))] current_path_indices = current_path_indices[:-1] if show_progress: if printed_match: # Write the path (without current_index_str) since we printed out a match # e.g. m/12381/8444/2/ sys.stdout.write(f"{current_path}") # lgtm [py/clear-text-logging-sensitive-data] # Remove the last index from the output else: _clear_line_part(len(current_index_str)) return found_search_terms
async def test_wallet_make_transaction(self, two_wallet_nodes): test_rpc_port = uint16(21529) 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) 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) 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, ) 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_node = await FullNodeRpcClient.create(self_hostname, test_rpc_port_node, bt.root_path, config) try: 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) 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"] 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 signed_tx_amount = 888000 tx_res = await client.create_signed_transaction([{"amount": signed_tx_amount, "puzzle_hash": ph_3}]) assert tx_res["success"] assert tx_res["signed_tx"]["fee_amount"] == 0 assert tx_res["signed_tx"]["amount"] == signed_tx_amount assert len(tx_res["signed_tx"]["additions"]) == 2 # The output and the change assert any([addition["amount"] == signed_tx_amount for addition in tx_res["signed_tx"]["additions"]]) push_res = await client_node.push_tx(SpendBundle.from_json_dict(tx_res["signed_tx"]["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["signed_tx"]["additions"]: if addition["amount"] != signed_tx_amount: coin_to_spend = Coin.from_json_dict(addition) assert coin_to_spend is not None tx_res = await client.create_signed_transaction( [{"amount": 444, "puzzle_hash": ph_4}, {"amount": 999, "puzzle_hash": ph_5}], coins=[coin_to_spend], fee=100, ) assert tx_res["success"] assert tx_res["signed_tx"]["fee_amount"] == 100 assert tx_res["signed_tx"]["amount"] == 444 + 999 assert len(tx_res["signed_tx"]["additions"]) == 3 # The outputs and the change assert any([addition["amount"] == 444 for addition in tx_res["signed_tx"]["additions"]]) assert any([addition["amount"] == 999 for addition in tx_res["signed_tx"]["additions"]]) assert ( sum([rem["amount"] for rem in tx_res["signed_tx"]["removals"]]) - sum([ad["amount"] for ad in tx_res["signed_tx"]["additions"]]) == 100 ) push_res = await client_node.push_tx(SpendBundle.from_json_dict(tx_res["signed_tx"]["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) await time_out_assert( 5, eventual_balance, initial_funds_eventually - tx_amount - signed_tx_amount - 444 - 999 - 100 ) address = await client.get_next_address("1", True) assert len(address) > 10 transactions = await client.get_transactions("1") assert len(transactions) > 1 pks = await client.get_public_keys() assert len(pks) == 1 assert (await client.get_height_info()) > 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] 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 test_wallet_backup_path = Path("test_wallet_backup_file") await client.create_backup(test_wallet_backup_path) assert test_wallet_backup_path.exists() test_wallet_backup_path.unlink() try: await client.send_transaction(wallets[0]["id"], 100, addr) raise Exception("Should not create tx if no balance") except ValueError: pass 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_node.close() await client.await_closed() await client_node.await_closed() await rpc_cleanup() await rpc_cleanup_node()
async def test1(self, simulation): test_rpc_port = uint16(21522) test_rpc_port_2 = uint16(21523) harvester_service, farmer_service = simulation harvester = harvester_service._node farmer_api = farmer_service._api def stop_node_cb(): pass def stop_node_cb_2(): pass config = bt.config hostname = config["self_hostname"] daemon_port = config["daemon_port"] farmer_rpc_api = FarmerRpcApi(farmer_api.farmer) harvester_rpc_api = HarvesterRpcApi(harvester) rpc_cleanup = await start_rpc_server( farmer_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( harvester_rpc_api, hostname, daemon_port, test_rpc_port_2, stop_node_cb_2, bt.root_path, config, connect_to_daemon=False, ) try: client = await FarmerRpcClient.create(self_hostname, test_rpc_port, bt.root_path, config) client_2 = await HarvesterRpcClient.create(self_hostname, test_rpc_port_2, bt.root_path, config) await validate_get_routes(client, farmer_rpc_api) await validate_get_routes(client_2, harvester_rpc_api) async def have_connections(): return len(await client.get_connections()) > 0 await time_out_assert(15, have_connections, True) assert (await client.get_signage_point(std_hash(b"2"))) is None assert len(await client.get_signage_points()) == 0 async def have_signage_points(): return len(await client.get_signage_points()) > 0 sp = farmer_protocol.NewSignagePoint(std_hash(b"1"), std_hash(b"2"), std_hash(b"3"), uint64(1), uint64(1000000), uint8(2)) await farmer_api.new_signage_point(sp) await time_out_assert(5, have_signage_points, True) assert (await client.get_signage_point(std_hash(b"2"))) is not None async def have_plots(): return len((await client_2.get_plots())["plots"]) > 0 await time_out_assert(5, have_plots, True) res = await client_2.get_plots() num_plots = len(res["plots"]) assert num_plots > 0 plot_dir = get_plot_dir() / "subdir" plot_dir.mkdir(parents=True, exist_ok=True) plot_dir_sub = get_plot_dir() / "subdir" / "subsubdir" plot_dir_sub.mkdir(parents=True, exist_ok=True) plotter = DiskPlotter() filename = "test_farmer_harvester_rpc_plot.plot" filename_2 = "test_farmer_harvester_rpc_plot2.plot" plotter.create_plot_disk( str(plot_dir), str(plot_dir), str(plot_dir), filename, 18, stream_plot_info_pk(bt.pool_pk, bt.farmer_pk, AugSchemeMPL.key_gen(bytes([4] * 32))), token_bytes(32), 128, 0, 2000, 0, False, ) # Making a plot with a puzzle hash encoded into it instead of pk plot_id_2 = token_bytes(32) plotter.create_plot_disk( str(plot_dir), str(plot_dir), str(plot_dir), filename_2, 18, stream_plot_info_ph(std_hash(b"random ph"), bt.farmer_pk, AugSchemeMPL.key_gen(bytes([5] * 32))), plot_id_2, 128, 0, 2000, 0, False, ) # Making the same plot, in a different dir. This should not be farmed plotter.create_plot_disk( str(plot_dir_sub), str(plot_dir_sub), str(plot_dir_sub), filename_2, 18, stream_plot_info_ph(std_hash(b"random ph"), bt.farmer_pk, AugSchemeMPL.key_gen(bytes([5] * 32))), plot_id_2, 128, 0, 2000, 0, False, ) res_2 = await client_2.get_plots() assert len(res_2["plots"]) == num_plots # Reset cache and force updates cache every second to make sure the farmer gets the most recent data update_interval_before = farmer_api.farmer.update_harvester_cache_interval farmer_api.farmer.update_harvester_cache_interval = 1 farmer_api.farmer.harvester_cache = {} # Test farmer get_harvesters async def test_get_harvesters(): harvester.plot_manager.trigger_refresh() await time_out_assert(5, harvester.plot_manager.needs_refresh, value=False) farmer_res = await client.get_harvesters() if len(list(farmer_res["harvesters"])) != 1: log.error( f"test_get_harvesters: invalid harvesters {list(farmer_res['harvesters'])}" ) return False if len(list( farmer_res["harvesters"][0]["plots"])) != num_plots: log.error( f"test_get_harvesters: invalid plots {list(farmer_res['harvesters'])}" ) return False return True await time_out_assert_custom_interval(30, 1, test_get_harvesters) # Reset cache and reset update interval to avoid hitting the rate limit farmer_api.farmer.update_harvester_cache_interval = update_interval_before farmer_api.farmer.harvester_cache = {} targets_1 = await client.get_reward_targets(False) assert "have_pool_sk" not in targets_1 assert "have_farmer_sk" not in targets_1 targets_2 = await client.get_reward_targets(True) assert targets_2["have_pool_sk"] and targets_2["have_farmer_sk"] new_ph: bytes32 = create_puzzlehash_for_pk( master_sk_to_wallet_sk(bt.farmer_master_sk, uint32(10)).get_g1()) new_ph_2: bytes32 = create_puzzlehash_for_pk( master_sk_to_wallet_sk(bt.pool_master_sk, uint32(472)).get_g1()) await client.set_reward_targets( encode_puzzle_hash(new_ph, "xch"), encode_puzzle_hash(new_ph_2, "xch")) targets_3 = await client.get_reward_targets(True) assert decode_puzzle_hash(targets_3["farmer_target"]) == new_ph assert decode_puzzle_hash(targets_3["pool_target"]) == new_ph_2 assert targets_3["have_pool_sk"] and targets_3["have_farmer_sk"] new_ph_3: bytes32 = create_puzzlehash_for_pk( master_sk_to_wallet_sk(bt.pool_master_sk, uint32(1888)).get_g1()) await client.set_reward_targets( None, encode_puzzle_hash(new_ph_3, "xch")) targets_4 = await client.get_reward_targets(True) assert decode_puzzle_hash(targets_4["farmer_target"]) == new_ph assert decode_puzzle_hash(targets_4["pool_target"]) == new_ph_3 assert not targets_4["have_pool_sk"] and targets_3["have_farmer_sk"] root_path = farmer_api.farmer._root_path config = load_config(root_path, "config.yaml") assert config["farmer"][ "xch_target_address"] == encode_puzzle_hash(new_ph, "xch") assert config["pool"]["xch_target_address"] == encode_puzzle_hash( new_ph_3, "xch") new_ph_3_encoded = encode_puzzle_hash(new_ph_3, "xch") added_char = new_ph_3_encoded + "a" with pytest.raises(ValueError): await client.set_reward_targets(None, added_char) replaced_char = new_ph_3_encoded[0:-1] + "a" with pytest.raises(ValueError): await client.set_reward_targets(None, replaced_char) assert len((await client.get_pool_state())["pool_state"]) == 0 all_sks = farmer_api.farmer.local_keychain.get_all_private_keys() auth_sk = master_sk_to_pooling_authentication_sk( all_sks[0][0], 2, 1) pool_list = [{ "launcher_id": "ae4ef3b9bfe68949691281a015a9c16630fc8f66d48c19ca548fb80768791afa", "authentication_public_key": bytes(auth_sk.get_g1()).hex(), "owner_public_key": "84c3fcf9d5581c1ddc702cb0f3b4a06043303b334dd993ab42b2c320ebfa98e5ce558448615b3f69638ba92cf7f43da5", # noqa "payout_instructions": "c2b08e41d766da4116e388357ed957d04ad754623a915f3fd65188a8746cf3e8", "pool_url": "localhost", "p2_singleton_puzzle_hash": "16e4bac26558d315cded63d4c5860e98deb447cc59146dd4de06ce7394b14f17", "target_puzzle_hash": "344587cf06a39db471d2cc027504e8688a0a67cce961253500c956c73603fd58", }] config["pool"]["pool_list"] = pool_list save_config(root_path, "config.yaml", config) await farmer_api.farmer.update_pool_state() pool_state = (await client.get_pool_state())["pool_state"] assert len(pool_state) == 1 assert ( pool_state[0]["pool_config"]["payout_instructions"] == "c2b08e41d766da4116e388357ed957d04ad754623a915f3fd65188a8746cf3e8" ) await client.set_payout_instructions( hexstr_to_bytes(pool_state[0]["pool_config"]["launcher_id"]), "1234vy") await farmer_api.farmer.update_pool_state() pool_state = (await client.get_pool_state())["pool_state"] assert pool_state[0]["pool_config"][ "payout_instructions"] == "1234vy" now = time.time() # Big arbitrary numbers used to be unlikely to accidentally collide. before_24h = (now - (25 * 60 * 60), 29984713) since_24h = (now - (23 * 60 * 60), 93049817) for p2_singleton_puzzle_hash, pool_dict in farmer_api.farmer.pool_state.items( ): for key in ["points_found_24h", "points_acknowledged_24h"]: pool_dict[key].insert(0, since_24h) pool_dict[key].insert(0, before_24h) sp = farmer_protocol.NewSignagePoint(std_hash(b"1"), std_hash(b"2"), std_hash(b"3"), uint64(1), uint64(1000000), uint8(2)) await farmer_api.new_signage_point(sp) client_pool_state = await client.get_pool_state() for pool_dict in client_pool_state["pool_state"]: for key in ["points_found_24h", "points_acknowledged_24h"]: assert pool_dict[key][0] == list(since_24h) finally: # Checks that the RPC manages to stop the node client.close() client_2.close() await client.await_closed() await client_2.await_closed() await rpc_cleanup() await rpc_cleanup_2()
def encode_cmd(puzzle_hash, prefix): print(encode_puzzle_hash(bytes.fromhex(puzzle_hash), prefix))
async def pprint_pool_wallet_state( wallet_client: WalletRpcClient, wallet_id: int, pool_wallet_info: PoolWalletInfo, address_prefix: str, pool_state_dict: Dict, plot_counts: Counter, ): if pool_wallet_info.current.state == PoolSingletonState.LEAVING_POOL and pool_wallet_info.target is None: expected_leave_height = pool_wallet_info.singleton_block_height + pool_wallet_info.current.relative_lock_height print( f"Current state: INVALID_STATE. Please leave/join again after block height {expected_leave_height}" ) else: print( f"Current state: {PoolSingletonState(pool_wallet_info.current.state).name}" ) print( f"Current state from block height: {pool_wallet_info.singleton_block_height}" ) print(f"Launcher ID: {pool_wallet_info.launcher_id}") print( "Target address (not for plotting): " f"{encode_puzzle_hash(pool_wallet_info.current.target_puzzle_hash, address_prefix)}" ) print( f"Number of plots: {plot_counts[pool_wallet_info.p2_singleton_puzzle_hash]}" ) print(f"Owner public key: {pool_wallet_info.current.owner_pubkey}") print( f"Pool contract address (use ONLY for plotting - do not send money to this address): " f"{encode_puzzle_hash(pool_wallet_info.p2_singleton_puzzle_hash, address_prefix)}" ) if pool_wallet_info.target is not None: print( f"Target state: {PoolSingletonState(pool_wallet_info.target.state).name}" ) print(f"Target pool URL: {pool_wallet_info.target.pool_url}") if pool_wallet_info.current.state == PoolSingletonState.SELF_POOLING.value: balances: Dict = await wallet_client.get_wallet_balance(str(wallet_id)) balance = balances["confirmed_wallet_balance"] typ = WalletType(int(WalletType.POOLING_WALLET)) address_prefix, scale = wallet_coin_unit(typ, address_prefix) print( f"Claimable balance: {print_balance(balance, scale, address_prefix)}" ) if pool_wallet_info.current.state == PoolSingletonState.FARMING_TO_POOL: print(f"Current pool URL: {pool_wallet_info.current.pool_url}") if pool_wallet_info.launcher_id in pool_state_dict: pool_state = pool_state_dict[pool_wallet_info.launcher_id] print( f"Current difficulty: {pool_state_dict[pool_wallet_info.launcher_id]['current_difficulty']}" ) print( f"Points balance: {pool_state_dict[pool_wallet_info.launcher_id]['current_points']}" ) points_found_24h = [ points for timestamp, points in pool_state["points_found_24h"] ] points_acknowledged_24h = [ points for timestamp, points in pool_state["points_acknowledged_24h"] ] summed_points_found_24h = sum(points_found_24h) summed_points_acknowledged_24h = sum(points_acknowledged_24h) if summed_points_found_24h == 0: success_pct = 0.0 else: success_pct = summed_points_acknowledged_24h / summed_points_found_24h print(f"Points found (24h): {summed_points_found_24h}") print(f"Percent Successful Points (24h): {success_pct:.2%}") print( f"Relative lock height: {pool_wallet_info.current.relative_lock_height} blocks" ) payout_instructions: str = pool_state_dict[ pool_wallet_info.launcher_id]["pool_config"]["payout_instructions"] try: payout_address = encode_puzzle_hash( bytes32.fromhex(payout_instructions), address_prefix) print( f"Payout instructions (pool will pay to this address): {payout_address}" ) except Exception: print( f"Payout instructions (pool will pay you with this): {payout_instructions}" ) if pool_wallet_info.current.state == PoolSingletonState.LEAVING_POOL: expected_leave_height = pool_wallet_info.singleton_block_height + pool_wallet_info.current.relative_lock_height if pool_wallet_info.target is not None: print( f"Expected to leave after block height: {expected_leave_height}" )
async def test1(self, simulation): test_rpc_port = uint16(21522) test_rpc_port_2 = uint16(21523) harvester, farmer_api = simulation def stop_node_cb(): pass def stop_node_cb_2(): pass config = bt.config hostname = config["self_hostname"] daemon_port = config["daemon_port"] farmer_rpc_api = FarmerRpcApi(farmer_api.farmer) harvester_rpc_api = HarvesterRpcApi(harvester) rpc_cleanup = await start_rpc_server( farmer_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( harvester_rpc_api, hostname, daemon_port, test_rpc_port_2, stop_node_cb_2, bt.root_path, config, connect_to_daemon=False, ) try: client = await FarmerRpcClient.create(self_hostname, test_rpc_port, bt.root_path, config) client_2 = await HarvesterRpcClient.create(self_hostname, test_rpc_port_2, bt.root_path, config) async def have_connections(): return len(await client.get_connections()) > 0 await time_out_assert(15, have_connections, True) assert (await client.get_signage_point(std_hash(b"2"))) is None assert len(await client.get_signage_points()) == 0 async def have_signage_points(): return len(await client.get_signage_points()) > 0 sp = farmer_protocol.NewSignagePoint( std_hash(b"1"), std_hash(b"2"), std_hash(b"3"), uint64(1), uint64(1000000), uint8(2) ) await farmer_api.new_signage_point(sp) await time_out_assert(5, have_signage_points, True) assert (await client.get_signage_point(std_hash(b"2"))) is not None async def have_plots(): return len((await client_2.get_plots())["plots"]) > 0 await time_out_assert(5, have_plots, True) res = await client_2.get_plots() num_plots = len(res["plots"]) assert num_plots > 0 plot_dir = get_plot_dir() / "subdir" plot_dir.mkdir(parents=True, exist_ok=True) plot_dir_sub = get_plot_dir() / "subdir" / "subsubdir" plot_dir_sub.mkdir(parents=True, exist_ok=True) plotter = DiskPlotter() filename = "test_farmer_harvester_rpc_plot.plot" filename_2 = "test_farmer_harvester_rpc_plot2.plot" plotter.create_plot_disk( str(plot_dir), str(plot_dir), str(plot_dir), filename, 18, stream_plot_info_pk(bt.pool_pk, bt.farmer_pk, AugSchemeMPL.key_gen(bytes([4] * 32))), token_bytes(32), 128, 0, 2000, 0, False, ) # Making a plot with a puzzle hash encoded into it instead of pk plot_id_2 = token_bytes(32) plotter.create_plot_disk( str(plot_dir), str(plot_dir), str(plot_dir), filename_2, 18, stream_plot_info_ph(std_hash(b"random ph"), bt.farmer_pk, AugSchemeMPL.key_gen(bytes([5] * 32))), plot_id_2, 128, 0, 2000, 0, False, ) # Making the same plot, in a different dir. This should not be farmed plotter.create_plot_disk( str(plot_dir_sub), str(plot_dir_sub), str(plot_dir_sub), filename_2, 18, stream_plot_info_ph(std_hash(b"random ph"), bt.farmer_pk, AugSchemeMPL.key_gen(bytes([5] * 32))), plot_id_2, 128, 0, 2000, 0, False, ) res_2 = await client_2.get_plots() assert len(res_2["plots"]) == num_plots # Test farmer get_harvesters async def test_get_harvesters(): farmer_res = await client.get_harvesters() if len(list(farmer_res["harvesters"])) != 1: return False if len(list(farmer_res["harvesters"][0]["plots"])) != num_plots: return False return True await time_out_assert(30, test_get_harvesters) expected_result: PlotRefreshResult = PlotRefreshResult() def test_refresh_callback(refresh_result: PlotRefreshResult): assert refresh_result.loaded_plots == expected_result.loaded_plots assert refresh_result.removed_plots == expected_result.removed_plots assert refresh_result.processed_files == expected_result.processed_files assert refresh_result.remaining_files == expected_result.remaining_files harvester.plot_manager.set_refresh_callback(test_refresh_callback) async def test_case( trigger, expect_loaded, expect_removed, expect_processed, expected_directories, expect_total_plots ): expected_result.loaded_plots = expect_loaded expected_result.removed_plots = expect_removed expected_result.processed_files = expect_processed await trigger harvester.plot_manager.trigger_refresh() assert len(await client_2.get_plot_directories()) == expected_directories await time_out_assert(5, harvester.plot_manager.needs_refresh, value=False) result = await client_2.get_plots() assert len(result["plots"]) == expect_total_plots assert len(harvester.plot_manager.cache) == expect_total_plots assert len(harvester.plot_manager.failed_to_open_filenames) == 0 # Add plot_dir with two new plots await test_case( client_2.add_plot_directory(str(plot_dir)), expect_loaded=2, expect_removed=0, expect_processed=2, expected_directories=2, expect_total_plots=num_plots + 2, ) # Add plot_dir_sub with one duplicate await test_case( client_2.add_plot_directory(str(plot_dir_sub)), expect_loaded=0, expect_removed=0, expect_processed=1, expected_directories=3, expect_total_plots=num_plots + 2, ) # Delete one plot await test_case( client_2.delete_plot(str(plot_dir / filename)), expect_loaded=0, expect_removed=1, expect_processed=0, expected_directories=3, expect_total_plots=num_plots + 1, ) # Remove directory with the duplicate await test_case( client_2.remove_plot_directory(str(plot_dir_sub)), expect_loaded=0, expect_removed=1, expect_processed=0, expected_directories=2, expect_total_plots=num_plots + 1, ) # Re-add the directory with the duplicate for other tests await test_case( client_2.add_plot_directory(str(plot_dir_sub)), expect_loaded=0, expect_removed=0, expect_processed=1, expected_directories=3, expect_total_plots=num_plots + 1, ) # Remove the directory which has the duplicated plot loaded. This removes the duplicated plot from plot_dir # and in the same run loads the plot from plot_dir_sub which is not longer seen as duplicate. await test_case( client_2.remove_plot_directory(str(plot_dir)), expect_loaded=1, expect_removed=1, expect_processed=1, expected_directories=2, expect_total_plots=num_plots + 1, ) # Re-add the directory now the plot seen as duplicate is from plot_dir, not from plot_dir_sub like before await test_case( client_2.add_plot_directory(str(plot_dir)), expect_loaded=0, expect_removed=0, expect_processed=1, expected_directories=3, expect_total_plots=num_plots + 1, ) # Remove the duplicated plot await test_case( client_2.delete_plot(str(plot_dir / filename_2)), expect_loaded=0, expect_removed=1, expect_processed=0, expected_directories=3, expect_total_plots=num_plots + 1, ) # Remove the directory with the loaded plot which is not longer a duplicate await test_case( client_2.remove_plot_directory(str(plot_dir_sub)), expect_loaded=0, expect_removed=1, expect_processed=0, expected_directories=2, expect_total_plots=num_plots, ) # Remove the directory which contains all other plots await test_case( client_2.remove_plot_directory(str(get_plot_dir())), expect_loaded=0, expect_removed=20, expect_processed=0, expected_directories=1, expect_total_plots=0, ) # Recover the plots to test caching # First make sure cache gets written if required and new plots are loaded await test_case( client_2.add_plot_directory(str(get_plot_dir())), expect_loaded=20, expect_removed=0, expect_processed=20, expected_directories=2, expect_total_plots=20, ) assert harvester.plot_manager.cache.path().exists() unlink(harvester.plot_manager.cache.path()) # Should not write the cache again on shutdown because it didn't change assert not harvester.plot_manager.cache.path().exists() harvester.plot_manager.stop_refreshing() assert not harvester.plot_manager.cache.path().exists() # Manually trigger `save_cache` and make sure it creates a new cache file harvester.plot_manager.cache.save() assert harvester.plot_manager.cache.path().exists() expected_result.loaded_plots = 20 expected_result.removed_plots = 0 expected_result.processed_files = 20 expected_result.remaining_files = 0 plot_manager: PlotManager = PlotManager(harvester.root_path, test_refresh_callback) plot_manager.start_refreshing() assert len(harvester.plot_manager.cache) == len(plot_manager.cache) await time_out_assert(5, plot_manager.needs_refresh, value=False) for path, plot_info in harvester.plot_manager.plots.items(): assert path in plot_manager.plots assert plot_manager.plots[path].prover.get_filename() == plot_info.prover.get_filename() assert plot_manager.plots[path].prover.get_id() == plot_info.prover.get_id() assert plot_manager.plots[path].prover.get_memo() == plot_info.prover.get_memo() assert plot_manager.plots[path].prover.get_size() == plot_info.prover.get_size() assert plot_manager.plots[path].pool_public_key == plot_info.pool_public_key assert plot_manager.plots[path].pool_contract_puzzle_hash == plot_info.pool_contract_puzzle_hash assert plot_manager.plots[path].plot_public_key == plot_info.plot_public_key assert plot_manager.plots[path].file_size == plot_info.file_size assert plot_manager.plots[path].time_modified == plot_info.time_modified assert harvester.plot_manager.plot_filename_paths == plot_manager.plot_filename_paths assert harvester.plot_manager.failed_to_open_filenames == plot_manager.failed_to_open_filenames assert harvester.plot_manager.no_key_filenames == plot_manager.no_key_filenames plot_manager.stop_refreshing() # Modify the content of the plot_manager.dat with open(harvester.plot_manager.cache.path(), "r+b") as file: file.write(b"\xff\xff") # Sets Cache.version to 65535 # Make sure it just loads the plots normally if it fails to load the cache plot_manager = PlotManager(harvester.root_path, test_refresh_callback) plot_manager.cache.load() assert len(plot_manager.cache) == 0 plot_manager.set_public_keys( harvester.plot_manager.farmer_public_keys, harvester.plot_manager.pool_public_keys ) expected_result.loaded_plots = 20 expected_result.removed_plots = 0 expected_result.processed_files = 20 expected_result.remaining_files = 0 plot_manager.start_refreshing() await time_out_assert(5, plot_manager.needs_refresh, value=False) assert len(plot_manager.plots) == len(harvester.plot_manager.plots) plot_manager.stop_refreshing() # Test re-trying if processing a plot failed # First save the plot retry_test_plot = Path(plot_dir_sub / filename_2).resolve() retry_test_plot_save = Path(plot_dir_sub / "save").resolve() copy(retry_test_plot, retry_test_plot_save) # Invalidate the plot with open(plot_dir_sub / filename_2, "r+b") as file: file.write(bytes(100)) # Add it and validate it fails to load await harvester.add_plot_directory(str(plot_dir_sub)) expected_result.loaded_plots = 0 expected_result.removed_plots = 0 expected_result.processed_files = 1 expected_result.remaining_files = 0 harvester.plot_manager.start_refreshing() await time_out_assert(5, harvester.plot_manager.needs_refresh, value=False) assert retry_test_plot in harvester.plot_manager.failed_to_open_filenames # Make sure the file stays in `failed_to_open_filenames` and doesn't get loaded or processed in the next # update round expected_result.loaded_plots = 0 expected_result.processed_files = 0 harvester.plot_manager.trigger_refresh() await time_out_assert(5, harvester.plot_manager.needs_refresh, value=False) assert retry_test_plot in harvester.plot_manager.failed_to_open_filenames # Now decrease the re-try timeout, restore the valid plot file and make sure it properly loads now harvester.plot_manager.refresh_parameter.retry_invalid_seconds = 0 move(retry_test_plot_save, retry_test_plot) expected_result.loaded_plots = 1 expected_result.processed_files = 1 harvester.plot_manager.trigger_refresh() await time_out_assert(5, harvester.plot_manager.needs_refresh, value=False) assert retry_test_plot not in harvester.plot_manager.failed_to_open_filenames targets_1 = await client.get_reward_targets(False) assert "have_pool_sk" not in targets_1 assert "have_farmer_sk" not in targets_1 targets_2 = await client.get_reward_targets(True) assert targets_2["have_pool_sk"] and targets_2["have_farmer_sk"] new_ph: bytes32 = create_puzzlehash_for_pk(master_sk_to_wallet_sk(bt.farmer_master_sk, uint32(10)).get_g1()) new_ph_2: bytes32 = create_puzzlehash_for_pk( master_sk_to_wallet_sk(bt.pool_master_sk, uint32(472)).get_g1() ) await client.set_reward_targets(encode_puzzle_hash(new_ph, "xch"), encode_puzzle_hash(new_ph_2, "xch")) targets_3 = await client.get_reward_targets(True) assert decode_puzzle_hash(targets_3["farmer_target"]) == new_ph assert decode_puzzle_hash(targets_3["pool_target"]) == new_ph_2 assert targets_3["have_pool_sk"] and targets_3["have_farmer_sk"] new_ph_3: bytes32 = create_puzzlehash_for_pk( master_sk_to_wallet_sk(bt.pool_master_sk, uint32(1888)).get_g1() ) await client.set_reward_targets(None, encode_puzzle_hash(new_ph_3, "xch")) targets_4 = await client.get_reward_targets(True) assert decode_puzzle_hash(targets_4["farmer_target"]) == new_ph assert decode_puzzle_hash(targets_4["pool_target"]) == new_ph_3 assert not targets_4["have_pool_sk"] and targets_3["have_farmer_sk"] root_path = farmer_api.farmer._root_path config = load_config(root_path, "config.yaml") assert config["farmer"]["xch_target_address"] == encode_puzzle_hash(new_ph, "xch") assert config["pool"]["xch_target_address"] == encode_puzzle_hash(new_ph_3, "xch") new_ph_3_encoded = encode_puzzle_hash(new_ph_3, "xch") added_char = new_ph_3_encoded + "a" with pytest.raises(ValueError): await client.set_reward_targets(None, added_char) replaced_char = new_ph_3_encoded[0:-1] + "a" with pytest.raises(ValueError): await client.set_reward_targets(None, replaced_char) assert len((await client.get_pool_state())["pool_state"]) == 0 all_sks = farmer_api.farmer.local_keychain.get_all_private_keys() auth_sk = master_sk_to_pooling_authentication_sk(all_sks[0][0], 2, 1) pool_list = [ { "launcher_id": "ae4ef3b9bfe68949691281a015a9c16630fc8f66d48c19ca548fb80768791afa", "authentication_public_key": bytes(auth_sk.get_g1()).hex(), "owner_public_key": "84c3fcf9d5581c1ddc702cb0f3b4a06043303b334dd993ab42b2c320ebfa98e5ce558448615b3f69638ba92cf7f43da5", "payout_instructions": "c2b08e41d766da4116e388357ed957d04ad754623a915f3fd65188a8746cf3e8", "pool_url": "localhost", "p2_singleton_puzzle_hash": "16e4bac26558d315cded63d4c5860e98deb447cc59146dd4de06ce7394b14f17", "target_puzzle_hash": "344587cf06a39db471d2cc027504e8688a0a67cce961253500c956c73603fd58", } ] config["pool"]["pool_list"] = pool_list save_config(root_path, "config.yaml", config) await farmer_api.farmer.update_pool_state() pool_state = (await client.get_pool_state())["pool_state"] assert len(pool_state) == 1 assert ( pool_state[0]["pool_config"]["payout_instructions"] == "c2b08e41d766da4116e388357ed957d04ad754623a915f3fd65188a8746cf3e8" ) await client.set_payout_instructions(hexstr_to_bytes(pool_state[0]["pool_config"]["launcher_id"]), "1234vy") await farmer_api.farmer.update_pool_state() pool_state = (await client.get_pool_state())["pool_state"] assert pool_state[0]["pool_config"]["payout_instructions"] == "1234vy" finally: # Checks that the RPC manages to stop the node client.close() client_2.close() await client.await_closed() await client_2.await_closed() await rpc_cleanup() await rpc_cleanup_2()
async def show_async( rpc_port: int, state: bool, show_connections: bool, exit_node: bool, add_connection: str, remove_connection: str, block_header_hash_by_height: str, block_by_header_hash: str, ) -> None: import time import traceback from time import localtime, struct_time from typing import List, Optional import aiohttp from chia.consensus.block_record import BlockRecord from chia.rpc.full_node_rpc_client import FullNodeRpcClient from chia.server.outbound_message import NodeType from chia.types.full_block import FullBlock from chia.util.bech32m import encode_puzzle_hash from chia.util.byte_types import hexstr_to_bytes from chia.util.config import load_config from chia.util.default_root import DEFAULT_ROOT_PATH from chia.util.ints import uint16 try: config = load_config(DEFAULT_ROOT_PATH, "config.yaml") self_hostname = config["self_hostname"] if rpc_port is None: rpc_port = config["full_node"]["rpc_port"] client = await FullNodeRpcClient.create(self_hostname, uint16(rpc_port), DEFAULT_ROOT_PATH, config) if state: blockchain_state = await client.get_blockchain_state() if blockchain_state is None: print("There is no blockchain found yet. Try again shortly") return peak: Optional[BlockRecord] = blockchain_state["peak"] difficulty = blockchain_state["difficulty"] sub_slot_iters = blockchain_state["sub_slot_iters"] synced = blockchain_state["sync"]["synced"] sync_mode = blockchain_state["sync"]["sync_mode"] total_iters = peak.total_iters if peak is not None else 0 num_blocks: int = 10 if sync_mode: sync_max_block = blockchain_state["sync"]["sync_tip_height"] sync_current_block = blockchain_state["sync"][ "sync_progress_height"] print( "Current Blockchain Status: Full Node syncing to block", sync_max_block, "\nCurrently synced to block:", sync_current_block, ) if synced: print("Current Blockchain Status: Full Node Synced") print("\nPeak: Hash:", peak.header_hash if peak is not None else "") elif peak is not None: print( f"Current Blockchain Status: Not Synced. Peak height: {peak.height}" ) else: print("\nSearching for an initial chain\n") print( "You may be able to expedite with 'chia show -a host:port' using a known node.\n" ) if peak is not None: if peak.is_transaction_block: peak_time = peak.timestamp else: peak_hash = peak.header_hash curr = await client.get_block_record(peak_hash) while curr is not None and not curr.is_transaction_block: curr = await client.get_block_record(curr.prev_hash) peak_time = curr.timestamp peak_time_struct = struct_time(localtime(peak_time)) print( " Time:", f"{time.strftime('%a %b %d %Y %T %Z', peak_time_struct)}", f" Height: {peak.height:>10}\n", ) print("Estimated network space: ", end="") network_space_human_readable = blockchain_state[ "space"] / 1024**4 if network_space_human_readable >= 1024: network_space_human_readable = network_space_human_readable / 1024 print(f"{network_space_human_readable:.3f} PiB") else: print(f"{network_space_human_readable:.3f} TiB") print(f"Current difficulty: {difficulty}") print(f"Current VDF sub_slot_iters: {sub_slot_iters}") print("Total iterations since the start of the blockchain:", total_iters) print("") print(" Height: | Hash:") added_blocks: List[BlockRecord] = [] curr = await client.get_block_record(peak.header_hash) while curr is not None and len( added_blocks) < num_blocks and curr.height > 0: added_blocks.append(curr) curr = await client.get_block_record(curr.prev_hash) for b in added_blocks: print(f"{b.height:>9} | {b.header_hash}") else: print("Blockchain has no blocks yet") # if called together with show_connections, leave a blank line if show_connections: print("") if show_connections: connections = await client.get_connections() print("Connections:") print( "Type IP Ports NodeID Last Connect" + " MiB Up|Dwn") for con in connections: last_connect_tuple = struct_time( localtime(con["last_message_time"])) last_connect = time.strftime("%b %d %T", last_connect_tuple) mb_down = con["bytes_read"] / (1024 * 1024) mb_up = con["bytes_written"] / (1024 * 1024) host = con["peer_host"] # Strip IPv6 brackets if host[0] == "[": host = host[1:39] # Nodetype length is 9 because INTRODUCER will be deprecated if NodeType(con["type"]) is NodeType.FULL_NODE: peak_height = con["peak_height"] peak_hash = con["peak_hash"] if peak_hash is None: peak_hash = "No Info" if peak_height is None: peak_height = 0 con_str = ( f"{NodeType(con['type']).name:9} {host:38} " f"{con['peer_port']:5}/{con['peer_server_port']:<5}" f" {con['node_id'].hex()[:8]}... " f"{last_connect} " f"{mb_up:7.1f}|{mb_down:<7.1f}" f"\n " f"-SB Height: {peak_height:8.0f} -Hash: {peak_hash[2:10]}..." ) else: con_str = ( f"{NodeType(con['type']).name:9} {host:38} " f"{con['peer_port']:5}/{con['peer_server_port']:<5}" f" {con['node_id'].hex()[:8]}... " f"{last_connect} " f"{mb_up:7.1f}|{mb_down:<7.1f}") print(con_str) # if called together with state, leave a blank line if state: print("") if exit_node: node_stop = await client.stop_node() print(node_stop, "Node stopped") if add_connection: if ":" not in add_connection: print( "Enter a valid IP and port in the following format: 10.5.4.3:8000" ) else: ip, port = ( ":".join(add_connection.split(":")[:-1]), add_connection.split(":")[-1], ) print(f"Connecting to {ip}, {port}") try: await client.open_connection(ip, int(port)) except Exception: print(f"Failed to connect to {ip}:{port}") if remove_connection: result_txt = "" if len(remove_connection) != 8: result_txt = "Invalid NodeID. Do not include '.'" else: connections = await client.get_connections() for con in connections: if remove_connection == con["node_id"].hex()[:8]: print("Attempting to disconnect", "NodeID", remove_connection) try: await client.close_connection(con["node_id"]) except Exception: result_txt = f"Failed to disconnect NodeID {remove_connection}" else: result_txt = f"NodeID {remove_connection}... {NodeType(con['type']).name} " f"{con['peer_host']} disconnected" elif result_txt == "": result_txt = f"NodeID {remove_connection}... not found" print(result_txt) if block_header_hash_by_height != "": block_header = await client.get_block_record_by_height( block_header_hash_by_height) if block_header is not None: print(f"Header hash of block {block_header_hash_by_height}: " f"{block_header.header_hash.hex()}") else: print("Block height", block_header_hash_by_height, "not found") if block_by_header_hash != "": block: Optional[BlockRecord] = await client.get_block_record( hexstr_to_bytes(block_by_header_hash)) full_block: Optional[FullBlock] = await client.get_block( hexstr_to_bytes(block_by_header_hash)) # Would like to have a verbose flag for this if block is not None: assert full_block is not None prev_b = await client.get_block_record(block.prev_hash) if prev_b is not None: difficulty = block.weight - prev_b.weight else: difficulty = block.weight if block.is_transaction_block: assert full_block.transactions_info is not None block_time = struct_time( localtime( full_block.foliage_transaction_block.timestamp if full_block.foliage_transaction_block else None)) block_time_string = time.strftime("%a %b %d %Y %T %Z", block_time) cost = str(full_block.transactions_info.cost) tx_filter_hash = "Not a transaction block" if full_block.foliage_transaction_block: tx_filter_hash = full_block.foliage_transaction_block.filter_hash fees: Any = block.fees else: block_time_string = "Not a transaction block" cost = "Not a transaction block" tx_filter_hash = "Not a transaction block" fees = "Not a transaction block" address_prefix = config["network_overrides"]["config"][ config["selected_network"]]["address_prefix"] farmer_address = encode_puzzle_hash(block.farmer_puzzle_hash, address_prefix) pool_address = encode_puzzle_hash(block.pool_puzzle_hash, address_prefix) pool_pk = (full_block.reward_chain_block.proof_of_space. pool_public_key if full_block.reward_chain_block. proof_of_space.pool_public_key is not None else "Pay to pool puzzle hash") print( f"Block Height {block.height}\n" f"Header Hash 0x{block.header_hash.hex()}\n" f"Timestamp {block_time_string}\n" f"Weight {block.weight}\n" f"Previous Block 0x{block.prev_hash.hex()}\n" f"Difficulty {difficulty}\n" f"Sub-slot iters {block.sub_slot_iters}\n" f"Cost {cost}\n" f"Total VDF Iterations {block.total_iters}\n" f"Is a Transaction Block?{block.is_transaction_block}\n" f"Deficit {block.deficit}\n" f"PoSpace 'k' Size {full_block.reward_chain_block.proof_of_space.size}\n" f"Plot Public Key 0x{full_block.reward_chain_block.proof_of_space.plot_public_key}\n" f"Pool Public Key {pool_pk}\n" f"Tx Filter Hash {tx_filter_hash}\n" f"Farmer Address {farmer_address}\n" f"Pool Address {pool_address}\n" f"Fees Amount {fees}\n") else: print("Block with header hash", block_header_hash_by_height, "not found") except Exception as e: if isinstance(e, aiohttp.client_exceptions.ClientConnectorError): print( f"Connection error. Check if full node rpc is running at {rpc_port}" ) print("This is normal if full node is still starting up") else: tb = traceback.format_exc() print(f"Exception from 'show' {tb}") client.close() await client.await_closed()
def check_keys(new_root: Path) -> None: keychain: Keychain = Keychain() all_sks = keychain.get_all_private_keys() if len(all_sks) == 0: print( "No keys are present in the keychain. Generate them with 'chia keys generate'" ) return config: Dict = load_config(new_root, "config.yaml") pool_child_pubkeys = [ master_sk_to_pool_sk(sk).get_g1() for sk, _ in all_sks ] all_targets = [] stop_searching_for_farmer = "xch_target_address" not in config["farmer"] stop_searching_for_pool = "xch_target_address" not in config["pool"] number_of_ph_to_search = 500 selected = config["selected_network"] prefix = config["network_overrides"]["config"][selected]["address_prefix"] for i in range(number_of_ph_to_search): if stop_searching_for_farmer and stop_searching_for_pool and i > 0: break for sk, _ in all_sks: all_targets.append( encode_puzzle_hash( create_puzzlehash_for_pk( master_sk_to_wallet_sk(sk, uint32(i)).get_g1()), prefix)) if all_targets[-1] == config["farmer"].get("xch_target_address"): stop_searching_for_farmer = True if all_targets[-1] == config["pool"].get("xch_target_address"): stop_searching_for_pool = True # Set the destinations if "xch_target_address" not in config["farmer"]: print( f"Setting the xch destination address for coinbase fees reward to {all_targets[0]}" ) config["farmer"]["xch_target_address"] = all_targets[0] elif config["farmer"]["xch_target_address"] not in all_targets: print( f"WARNING: using a farmer address which we don't have the private" f" keys for. We searched the first {number_of_ph_to_search} addresses. Consider overriding " f"{config['farmer']['xch_target_address']} with {all_targets[0]}") if "pool" not in config: config["pool"] = {} if "xch_target_address" not in config["pool"]: print( f"Setting the xch destination address for coinbase reward to {all_targets[0]}" ) config["pool"]["xch_target_address"] = all_targets[0] elif config["pool"]["xch_target_address"] not in all_targets: print( f"WARNING: using a pool address which we don't have the private" f" keys for. We searched the first {number_of_ph_to_search} addresses. Consider overriding " f"{config['pool']['xch_target_address']} with {all_targets[0]}") # Set the pool pks in the farmer pool_pubkeys_hex = set(bytes(pk).hex() for pk in pool_child_pubkeys) if "pool_public_keys" in config["farmer"]: for pk_hex in config["farmer"]["pool_public_keys"]: # Add original ones in config pool_pubkeys_hex.add(pk_hex) config["farmer"]["pool_public_keys"] = pool_pubkeys_hex save_config(new_root, "config.yaml", config)
async def test_create_rl_coin(self, three_wallet_nodes): num_blocks = 4 full_nodes, wallets = three_wallet_nodes full_node_api = full_nodes[0] full_node_server = full_node_api.server wallet_node, server_2 = wallets[0] wallet_node_1, wallet_server_1 = wallets[1] wallet_node_2, wallet_server_2 = wallets[2] wallet = wallet_node.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() await server_2.start_client(PeerInfo(self_hostname, uint16(full_node_server._port)), None) await wallet_server_1.start_client(PeerInfo(self_hostname, uint16(full_node_server._port)), None) await wallet_server_2.start_client(PeerInfo(self_hostname, uint16(full_node_server._port)), None) await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph)) for i in range(0, num_blocks + 1): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0")) fund_owners_initial_balance = await wallet.get_confirmed_balance() api_user = WalletRpcApi(wallet_node_1) val = await api_user.create_new_wallet( {"wallet_type": "rl_wallet", "rl_type": "user", "host": f"{self_hostname}:5000"} ) assert isinstance(val, dict) if "success" in val: assert val["success"] assert val["id"] assert val["type"] == WalletType.RATE_LIMITED.value user_wallet_id = val["id"] pubkey = val["pubkey"] api_admin = WalletRpcApi(wallet_node) val = await api_admin.create_new_wallet( { "wallet_type": "rl_wallet", "rl_type": "admin", "interval": 2, "limit": 10, "pubkey": pubkey, "amount": 100, "fee": 1, "host": f"{self_hostname}:5000", } ) assert isinstance(val, dict) if "success" in val: assert val["success"] assert val["id"] assert val["type"] == WalletType.RATE_LIMITED.value assert val["origin"] assert val["pubkey"] admin_wallet_id = val["id"] admin_pubkey = val["pubkey"] origin: Coin = val["origin"] await api_user.rl_set_user_info( { "wallet_id": user_wallet_id, "interval": 2, "limit": 10, "origin": { "parent_coin_info": origin.parent_coin_info.hex(), "puzzle_hash": origin.puzzle_hash.hex(), "amount": origin.amount, }, "admin_pubkey": admin_pubkey, } ) assert (await api_user.get_wallet_balance({"wallet_id": user_wallet_id}))["wallet_balance"][ "confirmed_wallet_balance" ] == 0 for i in range(0, 2 * num_blocks): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0")) assert await wallet.get_confirmed_balance() == fund_owners_initial_balance - 101 async def check_balance(api, wallet_id): balance_response = await api.get_wallet_balance({"wallet_id": wallet_id}) balance = balance_response["wallet_balance"]["confirmed_wallet_balance"] return balance await time_out_assert(15, check_balance, 100, api_user, user_wallet_id) receiving_wallet = wallet_node_2.wallet_state_manager.main_wallet address = encode_puzzle_hash(await receiving_wallet.get_new_puzzlehash(), "xch") assert await receiving_wallet.get_spendable_balance() == 0 val = await api_user.send_transaction({"wallet_id": user_wallet_id, "amount": 3, "fee": 2, "address": address}) assert "transaction_id" in val async def is_transaction_in_mempool(api, tx_id: bytes32) -> bool: try: val = await api.get_transaction({"wallet_id": user_wallet_id, "transaction_id": tx_id.hex()}) except ValueError: return False for _, mis, _ in val["transaction"].sent_to: if ( MempoolInclusionStatus(mis) == MempoolInclusionStatus.SUCCESS or MempoolInclusionStatus(mis) == MempoolInclusionStatus.PENDING ): return True return False await time_out_assert(15, is_transaction_in_mempool, True, api_user, val["transaction_id"]) for i in range(0, num_blocks): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0")) await time_out_assert(15, check_balance, 95, api_user, user_wallet_id) await time_out_assert(15, receiving_wallet.get_spendable_balance, 3) val = await api_admin.add_rate_limited_funds({"wallet_id": admin_wallet_id, "amount": 100, "fee": 7}) assert val["status"] == "SUCCESS" for i in range(0, 50): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0")) await time_out_assert(15, check_balance, 195, api_user, user_wallet_id) # test spending puzzle_hash = encode_puzzle_hash(await receiving_wallet.get_new_puzzlehash(), "xch") val = await api_user.send_transaction( { "wallet_id": user_wallet_id, "amount": 105, "fee": 0, "address": puzzle_hash, } ) await time_out_assert(15, is_transaction_in_mempool, True, api_user, val["transaction_id"]) for i in range(0, num_blocks): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0")) await time_out_assert(15, check_balance, 90, api_user, user_wallet_id) await time_out_assert(15, receiving_wallet.get_spendable_balance, 108) val = await api_admin.send_clawback_transaction({"wallet_id": admin_wallet_id, "fee": 11}) await time_out_assert(15, is_transaction_in_mempool, True, api_admin, val["transaction_id"]) for i in range(0, num_blocks): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0")) await time_out_assert(15, check_balance, 0, api_user, user_wallet_id) await time_out_assert(15, check_balance, 0, api_admin, user_wallet_id) final_balance = await wallet.get_confirmed_balance() assert final_balance == fund_owners_initial_balance - 129
def check_plots(root_path, num, challenge_start, grep_string, list_duplicates, debug_show_memo): config = load_config(root_path, "config.yaml") address_prefix = config["network_overrides"]["config"][ config["selected_network"]]["address_prefix"] plot_refresh_parameter: PlotsRefreshParameter = PlotsRefreshParameter( batch_sleep_milliseconds=0) plot_manager: PlotManager = PlotManager( root_path, match_str=grep_string, open_no_key_filenames=True, refresh_parameter=plot_refresh_parameter, refresh_callback=plot_refresh_callback, ) if num is not None: if num == 0: log.warning("Not opening plot files") else: if num < 5: log.warning( f"{num} challenges is too low, setting it to the minimum of 5" ) num = 5 if num < 30: log.warning( "Use 30 challenges (our default) for balance of speed and accurate results" ) else: num = 30 if challenge_start is not None: num_start = challenge_start num_end = num_start + num else: num_start = 0 num_end = num challenges = num_end - num_start if list_duplicates: log.warning("Checking for duplicate Plot IDs") log.info("Plot filenames expected to end with -[64 char plot ID].plot") if list_duplicates: all_filenames: List[Path] = [] for paths in get_plot_filenames(root_path).values(): all_filenames += paths find_duplicate_plot_IDs(all_filenames) if num == 0: return None parallel_read: bool = config["harvester"].get("parallel_read", True) v = Verifier() log.info( f"Loading plots in config.yaml using plot_manager loading code (parallel read: {parallel_read})\n" ) # Prompts interactively if the keyring is protected by a master passphrase. To use the daemon # for keychain access, KeychainProxy/connect_to_keychain should be used instead of Keychain. kc: Keychain = Keychain() plot_manager.set_public_keys( [ master_sk_to_farmer_sk(sk).get_g1() for sk, _ in kc.get_all_private_keys() ], [ G1Element.from_bytes(bytes.fromhex(pk)) for pk in config["farmer"]["pool_public_keys"] ], ) plot_manager.start_refreshing() while plot_manager.needs_refresh(): sleep(1) plot_manager.stop_refreshing() if plot_manager.plot_count() > 0: log.info("") log.info("") log.info(f"Starting to test each plot with {num} challenges each\n") total_good_plots: Counter = Counter() total_size = 0 bad_plots_list: List[Path] = [] with plot_manager: for plot_path, plot_info in plot_manager.plots.items(): pr = plot_info.prover log.info(f"Testing plot {plot_path} k={pr.get_size()}") if plot_info.pool_public_key is not None: log.info( f"\t{'Pool public key:':<23} {plot_info.pool_public_key}") if plot_info.pool_contract_puzzle_hash is not None: pca: str = encode_puzzle_hash( plot_info.pool_contract_puzzle_hash, address_prefix) log.info(f"\t{'Pool contract address:':<23} {pca}") # Look up local_sk from plot to save locked memory ( pool_public_key_or_puzzle_hash, farmer_public_key, local_master_sk, ) = parse_plot_info(pr.get_memo()) local_sk = master_sk_to_local_sk(local_master_sk) log.info(f"\t{'Farmer public key:' :<23} {farmer_public_key}") log.info(f"\t{'Local sk:' :<23} {local_sk}") total_proofs = 0 caught_exception: bool = False for i in range(num_start, num_end): challenge = std_hash(i.to_bytes(32, "big")) # Some plot errors cause get_qualities_for_challenge to throw a RuntimeError try: quality_start_time = int(round(time() * 1000)) for index, quality_str in enumerate( pr.get_qualities_for_challenge(challenge)): quality_spent_time = int(round( time() * 1000)) - quality_start_time if quality_spent_time > 5000: log.warning( f"\tLooking up qualities took: {quality_spent_time} ms. This should be below 5 seconds " f"to minimize risk of losing rewards.") else: log.info( f"\tLooking up qualities took: {quality_spent_time} ms." ) # Other plot errors cause get_full_proof or validate_proof to throw an AssertionError try: proof_start_time = int(round(time() * 1000)) proof = pr.get_full_proof(challenge, index, parallel_read) proof_spent_time = int(round( time() * 1000)) - proof_start_time if proof_spent_time > 15000: log.warning( f"\tFinding proof took: {proof_spent_time} ms. This should be below 15 seconds " f"to minimize risk of losing rewards.") else: log.info( f"\tFinding proof took: {proof_spent_time} ms" ) total_proofs += 1 ver_quality_str = v.validate_proof( pr.get_id(), pr.get_size(), challenge, proof) assert quality_str == ver_quality_str except AssertionError as e: log.error( f"{type(e)}: {e} error in proving/verifying for plot {plot_path}" ) caught_exception = True quality_start_time = int(round(time() * 1000)) except KeyboardInterrupt: log.warning("Interrupted, closing") return None except SystemExit: log.warning("System is shutting down.") return None except Exception as e: log.error( f"{type(e)}: {e} error in getting challenge qualities for plot {plot_path}" ) caught_exception = True if caught_exception is True: break if total_proofs > 0 and caught_exception is False: log.info( f"\tProofs {total_proofs} / {challenges}, {round(total_proofs/float(challenges), 4)}" ) total_good_plots[pr.get_size()] += 1 total_size += plot_path.stat().st_size else: log.error( f"\tProofs {total_proofs} / {challenges}, {round(total_proofs/float(challenges), 4)}" ) bad_plots_list.append(plot_path) log.info("") log.info("") log.info("Summary") total_plots: int = sum(list(total_good_plots.values())) log.info( f"Found {total_plots} valid plots, total size {total_size / (1024 * 1024 * 1024 * 1024):.5f} TiB" ) for (k, count) in sorted(dict(total_good_plots).items()): log.info(f"{count} plots of size {k}") grand_total_bad = len(bad_plots_list) + len( plot_manager.failed_to_open_filenames) if grand_total_bad > 0: log.warning(f"{grand_total_bad} invalid plots found:") if len(bad_plots_list) > 0: log.warning(f" {len(bad_plots_list)} bad plots:") for bad_plot_path in bad_plots_list: log.warning(f"{bad_plot_path}") if len(plot_manager.failed_to_open_filenames) > 0: log.warning( f" {len(plot_manager.failed_to_open_filenames)} unopenable plots:" ) for unopenable_plot_path in plot_manager.failed_to_open_filenames.keys( ): log.warning(f"{unopenable_plot_path}") if len(plot_manager.no_key_filenames) > 0: log.warning( f"There are {len(plot_manager.no_key_filenames)} plots with a farmer or pool public key that " f"is not on this machine. The farmer private key must be in the keychain in order to " f"farm them, use 'chia keys' to transfer keys. The pool public keys must be in the config.yaml" ) if debug_show_memo: plot_memo_str: str = "Plot Memos:\n" with plot_manager: for path, plot in plot_manager.plots.items(): plot_memo_str += f"{path}: {plot.prover.get_memo().hex()}\n" log.info(plot_memo_str)
async def test_wallet_make_transaction(self, two_wallet_nodes): test_rpc_port = uint16(21529) 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) 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) 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, ) 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) await validate_get_routes(client, wallet_rpc_api) client_node = await FullNodeRpcClient.create(self_hostname, test_rpc_port_node, bt.root_path, config) try: 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) 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"] 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 signed_tx_amount = 888000 tx_res: TransactionRecord = await client.create_signed_transaction( [{ "amount": signed_tx_amount, "puzzle_hash": ph_3 }]) 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_node.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 }, { "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) 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 }, { "amount": 666, "puzzle_hash": ph_5 }], 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 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)) == 2 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] fingerprint = await client.get_logged_in_fingerprint() assert 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 test_wallet_backup_path = Path("test_wallet_backup_file") await client.create_backup(test_wallet_backup_path) assert test_wallet_backup_path.exists() test_wallet_backup_path.unlink() try: await client.send_transaction(wallets[0]["id"], 100, addr) raise Exception("Should not create tx if no balance") except ValueError: pass 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_node.close() await client.await_closed() await client_node.await_closed() await rpc_cleanup() await rpc_cleanup_node()
async def pprint_pool_wallet_state( wallet_client: WalletRpcClient, wallet_id: int, pool_wallet_info: PoolWalletInfo, address_prefix: str, pool_state_dict: Dict, unconfirmed_transactions: List[TransactionRecord], ): if pool_wallet_info.current.state == PoolSingletonState.LEAVING_POOL and pool_wallet_info.target is None: expected_leave_height = pool_wallet_info.singleton_block_height + pool_wallet_info.current.relative_lock_height print( f"Current state: INVALID_STATE. Please leave/join again after block height {expected_leave_height}" ) else: print( f"Current state: {PoolSingletonState(pool_wallet_info.current.state).name}" ) print( f"Current state from block height: {pool_wallet_info.singleton_block_height}" ) print(f"Launcher ID: {pool_wallet_info.launcher_id}") print( "Target address (not for plotting): " f"{encode_puzzle_hash(pool_wallet_info.current.target_puzzle_hash, address_prefix)}" ) print(f"Owner public key: {pool_wallet_info.current.owner_pubkey}") print( f"P2 singleton address (pool contract address for plotting): " f"{encode_puzzle_hash(pool_wallet_info.p2_singleton_puzzle_hash, address_prefix)}" ) if pool_wallet_info.target is not None: print( f"Target state: {PoolSingletonState(pool_wallet_info.target.state).name}" ) print(f"Target pool URL: {pool_wallet_info.target.pool_url}") if pool_wallet_info.current.state == PoolSingletonState.SELF_POOLING.value: balances: Dict = await wallet_client.get_wallet_balance(str(wallet_id)) balance = balances["confirmed_wallet_balance"] typ = WalletType(int(WalletType.POOLING_WALLET)) address_prefix, scale = wallet_coin_unit(typ, address_prefix) print( f"Claimable balance: {print_balance(balance, scale, address_prefix)}" ) if pool_wallet_info.current.state == PoolSingletonState.FARMING_TO_POOL: print(f"Current pool URL: {pool_wallet_info.current.pool_url}") if pool_wallet_info.launcher_id in pool_state_dict: print( f"Current difficulty: {pool_state_dict[pool_wallet_info.launcher_id]['current_difficulty']}" ) print( f"Points balance: {pool_state_dict[pool_wallet_info.launcher_id]['current_points']}" ) print( f"Relative lock height: {pool_wallet_info.current.relative_lock_height} blocks" ) payout_instructions: str = pool_state_dict[ pool_wallet_info.launcher_id]["pool_config"]["payout_instructions"] try: payout_address = encode_puzzle_hash( bytes32.fromhex(payout_instructions), address_prefix) print( f"Payout instructions (pool will pay to this address): {payout_address}" ) except Exception: print( f"Payout instructions (pool will pay you with this): {payout_instructions}" ) if pool_wallet_info.current.state == PoolSingletonState.LEAVING_POOL: expected_leave_height = pool_wallet_info.singleton_block_height + pool_wallet_info.current.relative_lock_height if pool_wallet_info.target is not None: print( f"Expected to leave after block height: {expected_leave_height}" )
async def test_absorb_self(self, one_wallet_node_and_rpc): client, wallet_node_0, full_node_api = one_wallet_node_and_rpc wallet_0 = wallet_node_0.wallet_state_manager.main_wallet our_ph = await wallet_0.get_new_puzzlehash() summaries_response = await client.get_wallets() for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: assert False creation_tx: TransactionRecord = await client.create_new_pool_wallet( our_ph, "", 0, "localhost:5000", "new", "SELF_POOLING" ) await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, creation_tx.spend_bundle, creation_tx.name, ) await self.farm_blocks(full_node_api, our_ph, 1) await asyncio.sleep(2) status: PoolWalletInfo = (await client.pw_status(2))[0] assert status.current.state == PoolSingletonState.SELF_POOLING.value plot_id: bytes32 = self.create_pool_plot(status.p2_singleton_puzzle_hash) all_blocks = await full_node_api.get_all_full_blocks() blocks = bt.get_consecutive_blocks( 3, block_list_input=all_blocks, force_plot_id=plot_id, farmer_reward_puzzle_hash=our_ph, guarantee_transaction_block=True, ) await full_node_api.full_node.respond_block(full_node_protocol.RespondBlock(blocks[-3])) await full_node_api.full_node.respond_block(full_node_protocol.RespondBlock(blocks[-2])) await full_node_api.full_node.respond_block(full_node_protocol.RespondBlock(blocks[-1])) await asyncio.sleep(2) bal = await client.get_wallet_balance(2) assert bal["confirmed_wallet_balance"] == 2 * 1750000000000 # Claim 2 * 1.75, and farm a new 1.75 absorb_tx: TransactionRecord = await client.pw_absorb_rewards(2) await time_out_assert( 5, full_node_api.full_node.mempool_manager.get_spendbundle, absorb_tx.spend_bundle, absorb_tx.name, ) await self.farm_blocks(full_node_api, our_ph, 2) await asyncio.sleep(2) new_status: PoolWalletInfo = (await client.pw_status(2))[0] assert status.current == new_status.current assert status.tip_singleton_coin_id != new_status.tip_singleton_coin_id bal = await client.get_wallet_balance(2) assert bal["confirmed_wallet_balance"] == 1 * 1750000000000 # Claim another 1.75 absorb_tx: TransactionRecord = await client.pw_absorb_rewards(2) absorb_tx.spend_bundle.debug() await time_out_assert( 5, full_node_api.full_node.mempool_manager.get_spendbundle, absorb_tx.spend_bundle, absorb_tx.name, ) await self.farm_blocks(full_node_api, our_ph, 2) await asyncio.sleep(2) bal = await client.get_wallet_balance(2) assert bal["confirmed_wallet_balance"] == 0 assert len(await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(2)) == 0 tr: TransactionRecord = await client.send_transaction( 1, 100, encode_puzzle_hash(status.p2_singleton_puzzle_hash, "txch") ) await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, tr.spend_bundle, tr.name, ) await self.farm_blocks(full_node_api, our_ph, 2) # Balance ignores non coinbase TX bal = await client.get_wallet_balance(2) assert bal["confirmed_wallet_balance"] == 0 with pytest.raises(ValueError): await client.pw_absorb_rewards(2) self.delete_plot(plot_id)
async def test1(self, simulation): test_rpc_port = uint16(21522) test_rpc_port_2 = uint16(21523) harvester, farmer_api = simulation def stop_node_cb(): pass def stop_node_cb_2(): pass config = bt.config hostname = config["self_hostname"] daemon_port = config["daemon_port"] farmer_rpc_api = FarmerRpcApi(farmer_api.farmer) harvester_rpc_api = HarvesterRpcApi(harvester) rpc_cleanup = await start_rpc_server( farmer_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( harvester_rpc_api, hostname, daemon_port, test_rpc_port_2, stop_node_cb_2, bt.root_path, config, connect_to_daemon=False, ) try: client = await FarmerRpcClient.create(self_hostname, test_rpc_port, bt.root_path, config) client_2 = await HarvesterRpcClient.create(self_hostname, test_rpc_port_2, bt.root_path, config) async def have_connections(): return len(await client.get_connections()) > 0 await time_out_assert(15, have_connections, True) assert (await client.get_signage_point(std_hash(b"2"))) is None assert len(await client.get_signage_points()) == 0 async def have_signage_points(): return len(await client.get_signage_points()) > 0 sp = farmer_protocol.NewSignagePoint(std_hash(b"1"), std_hash(b"2"), std_hash(b"3"), uint64(1), uint64(1000000), uint8(2)) await farmer_api.new_signage_point(sp) await time_out_assert(5, have_signage_points, True) assert (await client.get_signage_point(std_hash(b"2"))) is not None async def have_plots(): return len((await client_2.get_plots())["plots"]) > 0 await time_out_assert(5, have_plots, True) res = await client_2.get_plots() num_plots = len(res["plots"]) assert num_plots > 0 plot_dir = get_plot_dir() / "subdir" plot_dir.mkdir(parents=True, exist_ok=True) plot_dir_sub = get_plot_dir() / "subdir" / "subsubdir" plot_dir_sub.mkdir(parents=True, exist_ok=True) plotter = DiskPlotter() filename = "test_farmer_harvester_rpc_plot.plot" filename_2 = "test_farmer_harvester_rpc_plot2.plot" plotter.create_plot_disk( str(plot_dir), str(plot_dir), str(plot_dir), filename, 18, stream_plot_info_pk(bt.pool_pk, bt.farmer_pk, AugSchemeMPL.key_gen(bytes([4] * 32))), token_bytes(32), 128, 0, 2000, 0, False, ) # Making a plot with a puzzle hash encoded into it instead of pk plot_id_2 = token_bytes(32) plotter.create_plot_disk( str(plot_dir), str(plot_dir), str(plot_dir), filename_2, 18, stream_plot_info_ph(std_hash(b"random ph"), bt.farmer_pk, AugSchemeMPL.key_gen(bytes([5] * 32))), plot_id_2, 128, 0, 2000, 0, False, ) # Making the same plot, in a different dir. This should not be farmed plotter.create_plot_disk( str(plot_dir_sub), str(plot_dir_sub), str(plot_dir_sub), filename_2, 18, stream_plot_info_ph(std_hash(b"random ph"), bt.farmer_pk, AugSchemeMPL.key_gen(bytes([5] * 32))), plot_id_2, 128, 0, 2000, 0, False, ) res_2 = await client_2.get_plots() assert len(res_2["plots"]) == num_plots # Test farmer get_plots farmer_res = await client.get_plots() assert len(list(farmer_res.values())[0]["plots"]) == num_plots assert len(await client_2.get_plot_directories()) == 1 await client_2.add_plot_directory(str(plot_dir)) await client_2.add_plot_directory(str(plot_dir_sub)) assert len(await client_2.get_plot_directories()) == 3 res_2 = await client_2.get_plots() assert len(res_2["plots"]) == num_plots + 2 await client_2.delete_plot(str(plot_dir / filename)) await client_2.delete_plot(str(plot_dir / filename_2)) await client_2.refresh_plots() res_3 = await client_2.get_plots() assert len(res_3["plots"]) == num_plots + 1 await client_2.remove_plot_directory(str(plot_dir)) assert len(await client_2.get_plot_directories()) == 2 targets_1 = await client.get_reward_targets(False) assert "have_pool_sk" not in targets_1 assert "have_farmer_sk" not in targets_1 targets_2 = await client.get_reward_targets(True) assert targets_2["have_pool_sk"] and targets_2["have_farmer_sk"] new_ph: bytes32 = create_puzzlehash_for_pk( master_sk_to_wallet_sk(bt.farmer_master_sk, uint32(10)).get_g1()) new_ph_2: bytes32 = create_puzzlehash_for_pk( master_sk_to_wallet_sk(bt.pool_master_sk, uint32(472)).get_g1()) await client.set_reward_targets( encode_puzzle_hash(new_ph, "xch"), encode_puzzle_hash(new_ph_2, "xch")) targets_3 = await client.get_reward_targets(True) assert decode_puzzle_hash(targets_3["farmer_target"]) == new_ph assert decode_puzzle_hash(targets_3["pool_target"]) == new_ph_2 assert targets_3["have_pool_sk"] and targets_3["have_farmer_sk"] new_ph_3: bytes32 = create_puzzlehash_for_pk( master_sk_to_wallet_sk(bt.pool_master_sk, uint32(1888)).get_g1()) await client.set_reward_targets( None, encode_puzzle_hash(new_ph_3, "xch")) targets_4 = await client.get_reward_targets(True) assert decode_puzzle_hash(targets_4["farmer_target"]) == new_ph assert decode_puzzle_hash(targets_4["pool_target"]) == new_ph_3 assert not targets_4["have_pool_sk"] and targets_3["have_farmer_sk"] root_path = farmer_api.farmer._root_path config = load_config(root_path, "config.yaml") assert config["farmer"][ "xch_target_address"] == encode_puzzle_hash(new_ph, "xch") assert config["pool"]["xch_target_address"] == encode_puzzle_hash( new_ph_3, "xch") new_ph_3_encoded = encode_puzzle_hash(new_ph_3, "xch") added_char = new_ph_3_encoded + "a" with pytest.raises(ValueError): await client.set_reward_targets(None, added_char) replaced_char = new_ph_3_encoded[0:-1] + "a" with pytest.raises(ValueError): await client.set_reward_targets(None, replaced_char) assert len((await client.get_pool_state())["pool_state"]) == 0 all_sks = farmer_api.farmer.keychain.get_all_private_keys() auth_sk = master_sk_to_pooling_authentication_sk( all_sks[0][0], 2, 1) pool_list = [{ "launcher_id": "ae4ef3b9bfe68949691281a015a9c16630fc8f66d48c19ca548fb80768791afa", "authentication_public_key": bytes(auth_sk.get_g1()).hex(), "owner_public_key": "84c3fcf9d5581c1ddc702cb0f3b4a06043303b334dd993ab42b2c320ebfa98e5ce558448615b3f69638ba92cf7f43da5", "payout_instructions": "c2b08e41d766da4116e388357ed957d04ad754623a915f3fd65188a8746cf3e8", "pool_url": "localhost", "p2_singleton_puzzle_hash": "16e4bac26558d315cded63d4c5860e98deb447cc59146dd4de06ce7394b14f17", "target_puzzle_hash": "344587cf06a39db471d2cc027504e8688a0a67cce961253500c956c73603fd58", }] config["pool"]["pool_list"] = pool_list save_config(root_path, "config.yaml", config) await farmer_api.farmer.update_pool_state() pool_state = (await client.get_pool_state())["pool_state"] assert len(pool_state) == 1 assert ( pool_state[0]["pool_config"]["payout_instructions"] == "c2b08e41d766da4116e388357ed957d04ad754623a915f3fd65188a8746cf3e8" ) await client.set_payout_instructions( hexstr_to_bytes(pool_state[0]["pool_config"]["launcher_id"]), "1234vy") pool_state = (await client.get_pool_state())["pool_state"] assert pool_state[0]["pool_config"][ "payout_instructions"] == "1234vy" finally: # Checks that the RPC manages to stop the node client.close() client_2.close() await client.await_closed() await client_2.await_closed() await rpc_cleanup() await rpc_cleanup_2()
async def test1(self, simulation): test_rpc_port = uint16(21522) test_rpc_port_2 = uint16(21523) harvester, farmer_api = simulation def stop_node_cb(): pass def stop_node_cb_2(): pass config = bt.config hostname = config["self_hostname"] daemon_port = config["daemon_port"] farmer_rpc_api = FarmerRpcApi(farmer_api.farmer) harvester_rpc_api = HarvesterRpcApi(harvester) rpc_cleanup = await start_rpc_server( farmer_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( harvester_rpc_api, hostname, daemon_port, test_rpc_port_2, stop_node_cb_2, bt.root_path, config, connect_to_daemon=False, ) try: client = await FarmerRpcClient.create(self_hostname, test_rpc_port, bt.root_path, config) client_2 = await HarvesterRpcClient.create(self_hostname, test_rpc_port_2, bt.root_path, config) async def have_connections(): return len(await client.get_connections()) > 0 await time_out_assert(15, have_connections, True) assert (await client.get_signage_point(std_hash(b"2"))) is None assert len(await client.get_signage_points()) == 0 async def have_signage_points(): return len(await client.get_signage_points()) > 0 sp = farmer_protocol.NewSignagePoint(std_hash(b"1"), std_hash(b"2"), std_hash(b"3"), uint64(1), uint64(1000000), uint8(2)) await farmer_api.new_signage_point(sp) await time_out_assert(5, have_signage_points, True) assert (await client.get_signage_point(std_hash(b"2"))) is not None async def have_plots(): return len((await client_2.get_plots())["plots"]) > 0 await time_out_assert(5, have_plots, True) res = await client_2.get_plots() num_plots = len(res["plots"]) assert num_plots > 0 plot_dir = get_plot_dir() / "subdir" plot_dir.mkdir(parents=True, exist_ok=True) plot_dir_sub = get_plot_dir() / "subdir" / "subsubdir" plot_dir_sub.mkdir(parents=True, exist_ok=True) plotter = DiskPlotter() filename = "test_farmer_harvester_rpc_plot.plot" filename_2 = "test_farmer_harvester_rpc_plot2.plot" plotter.create_plot_disk( str(plot_dir), str(plot_dir), str(plot_dir), filename, 18, stream_plot_info_pk(bt.pool_pk, bt.farmer_pk, AugSchemeMPL.key_gen(bytes([4] * 32))), token_bytes(32), 128, 0, 2000, 0, False, ) # Making a plot with a puzzle hash encoded into it instead of pk plot_id_2 = token_bytes(32) plotter.create_plot_disk( str(plot_dir), str(plot_dir), str(plot_dir), filename_2, 18, stream_plot_info_ph(std_hash(b"random ph"), bt.farmer_pk, AugSchemeMPL.key_gen(bytes([5] * 32))), plot_id_2, 128, 0, 2000, 0, False, ) # Making the same plot, in a different dir. This should not be farmed plotter.create_plot_disk( str(plot_dir_sub), str(plot_dir_sub), str(plot_dir_sub), filename_2, 18, stream_plot_info_ph(std_hash(b"random ph"), bt.farmer_pk, AugSchemeMPL.key_gen(bytes([5] * 32))), plot_id_2, 128, 0, 2000, 0, False, ) res_2 = await client_2.get_plots() assert len(res_2["plots"]) == num_plots assert len(await client_2.get_plot_directories()) == 1 await client_2.add_plot_directory(str(plot_dir)) await client_2.add_plot_directory(str(plot_dir_sub)) assert len(await client_2.get_plot_directories()) == 3 res_2 = await client_2.get_plots() assert len(res_2["plots"]) == num_plots + 2 await client_2.delete_plot(str(plot_dir / filename)) await client_2.delete_plot(str(plot_dir / filename_2)) await client_2.refresh_plots() res_3 = await client_2.get_plots() assert len(res_3["plots"]) == num_plots + 1 await client_2.remove_plot_directory(str(plot_dir)) assert len(await client_2.get_plot_directories()) == 2 targets_1 = await client.get_reward_targets(False) assert "have_pool_sk" not in targets_1 assert "have_farmer_sk" not in targets_1 targets_2 = await client.get_reward_targets(True) assert targets_2["have_pool_sk"] and targets_2["have_farmer_sk"] new_ph: bytes32 = create_puzzlehash_for_pk( master_sk_to_wallet_sk(bt.farmer_master_sk, uint32(10)).get_g1()) new_ph_2: bytes32 = create_puzzlehash_for_pk( master_sk_to_wallet_sk(bt.pool_master_sk, uint32(472)).get_g1()) await client.set_reward_targets( encode_puzzle_hash(new_ph, "xch"), encode_puzzle_hash(new_ph_2, "xch")) targets_3 = await client.get_reward_targets(True) assert decode_puzzle_hash(targets_3["farmer_target"]) == new_ph assert decode_puzzle_hash(targets_3["pool_target"]) == new_ph_2 assert targets_3["have_pool_sk"] and targets_3["have_farmer_sk"] new_ph_3: bytes32 = create_puzzlehash_for_pk( master_sk_to_wallet_sk(bt.pool_master_sk, uint32(1888)).get_g1()) await client.set_reward_targets( None, encode_puzzle_hash(new_ph_3, "xch")) targets_4 = await client.get_reward_targets(True) assert decode_puzzle_hash(targets_4["farmer_target"]) == new_ph assert decode_puzzle_hash(targets_4["pool_target"]) == new_ph_3 assert not targets_4["have_pool_sk"] and targets_3["have_farmer_sk"] root_path = farmer_api.farmer._root_path config = load_config(root_path, "config.yaml") assert config["farmer"][ "xch_target_address"] == encode_puzzle_hash(new_ph, "xch") assert config["pool"]["xch_target_address"] == encode_puzzle_hash( new_ph_3, "xch") new_ph_3_encoded = encode_puzzle_hash(new_ph_3, "xch") added_char = new_ph_3_encoded + "a" with pytest.raises(ValueError): await client.set_reward_targets(None, added_char) replaced_char = new_ph_3_encoded[0:-1] + "a" with pytest.raises(ValueError): await client.set_reward_targets(None, replaced_char) finally: # Checks that the RPC manages to stop the node client.close() client_2.close() await client.await_closed() await client_2.await_closed() await rpc_cleanup() await rpc_cleanup_2()
async def test_create_rl_coin(self, three_wallet_nodes): num_blocks = 4 full_nodes, wallets = three_wallet_nodes full_node_api = full_nodes[0] full_node_server = full_node_api.server wallet_node, server_2 = wallets[0] wallet_node_1, wallet_server_1 = wallets[1] wallet_node_2, wallet_server_2 = wallets[2] wallet = wallet_node.wallet_state_manager.main_wallet ph = await wallet.get_new_puzzlehash() await server_2.start_client( PeerInfo(self_hostname, uint16(full_node_server._port)), None) await wallet_server_1.start_client( PeerInfo(self_hostname, uint16(full_node_server._port)), None) await wallet_server_2.start_client( PeerInfo(self_hostname, uint16(full_node_server._port)), None) await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph) ) for i in range(0, num_blocks + 1): await full_node_api.farm_new_transaction_block( FarmNewBlockProtocol(32 * b"\0")) await time_out_assert(15, wallet_height_at_least, True, wallet_node, 6) fund_owners_initial_balance = await wallet.get_confirmed_balance() api_user = WalletRpcApi(wallet_node_1) val = await api_user.create_new_wallet({ "wallet_type": "rl_wallet", "rl_type": "user", "host": f"{self_hostname}:5000" }) assert isinstance(val, dict) if "success" in val: assert val["success"] assert val["id"] assert val["type"] == WalletType.RATE_LIMITED.value user_wallet_id = val["id"] pubkey = val["pubkey"] api_admin = WalletRpcApi(wallet_node) val = await api_admin.create_new_wallet({ "wallet_type": "rl_wallet", "rl_type": "admin", "interval": 2, "limit": 10, "pubkey": pubkey, "amount": 100, "fee": 1, "host": f"{self_hostname}:5000", }) assert isinstance(val, dict) if "success" in val: assert val["success"] assert val["id"] assert val["type"] == WalletType.RATE_LIMITED.value assert val["origin"] assert val["pubkey"] admin_wallet_id = val["id"] admin_pubkey = val["pubkey"] origin: Coin = val["origin"] await api_user.rl_set_user_info({ "wallet_id": user_wallet_id, "interval": 2, "limit": 10, "origin": { "parent_coin_info": origin.parent_coin_info.hex(), "puzzle_hash": origin.puzzle_hash.hex(), "amount": origin.amount, }, "admin_pubkey": admin_pubkey, }) assert (await api_user.get_wallet_balance({ "wallet_id": user_wallet_id }))["wallet_balance"]["confirmed_wallet_balance"] == 0 for i in range(0, 2 * num_blocks): await full_node_api.farm_new_transaction_block( FarmNewBlockProtocol(32 * b"\0")) await time_out_assert(15, wallet_height_at_least, True, wallet_node, 14) assert await wallet.get_confirmed_balance( ) == fund_owners_initial_balance - 101 assert await check_balance(api_user, user_wallet_id) == 100 receiving_wallet = wallet_node_2.wallet_state_manager.main_wallet address = encode_puzzle_hash( await receiving_wallet.get_new_puzzlehash(), "xch") assert await receiving_wallet.get_spendable_balance() == 0 val = await api_user.send_transaction({ "wallet_id": user_wallet_id, "amount": 3, "fee": 2, "address": address }) assert "transaction_id" in val await time_out_assert(15, is_transaction_in_mempool, True, user_wallet_id, api_user, val["transaction_id"]) for i in range(0, num_blocks): await full_node_api.farm_new_transaction_block( FarmNewBlockProtocol(32 * b"\0")) await time_out_assert(15, wallet_height_at_least, True, wallet_node, 18) assert await is_transaction_confirmed(user_wallet_id, api_user, val["transaction_id"]) assert await check_balance(api_user, user_wallet_id) == 95 assert await receiving_wallet.get_spendable_balance() == 3 val = await api_admin.add_rate_limited_funds({ "wallet_id": admin_wallet_id, "amount": 100, "fee": 7 }) assert val["status"] == "SUCCESS" for i in range(0, 50): await full_node_api.farm_new_transaction_block( FarmNewBlockProtocol(32 * b"\0")) await time_out_assert(15, wallet_height_at_least, True, wallet_node, 68) assert await check_balance(api_user, user_wallet_id) == 195 # test spending puzzle_hash = encode_puzzle_hash( await receiving_wallet.get_new_puzzlehash(), "xch") val = await api_user.send_transaction({ "wallet_id": user_wallet_id, "amount": 105, "fee": 0, "address": puzzle_hash }) await time_out_assert(15, is_transaction_in_mempool, True, user_wallet_id, api_user, val["transaction_id"]) for i in range(0, num_blocks): await full_node_api.farm_new_transaction_block( FarmNewBlockProtocol(32 * b"\0")) await time_out_assert(15, wallet_height_at_least, True, wallet_node, 72) assert await is_transaction_confirmed(user_wallet_id, api_user, val["transaction_id"]) assert await check_balance(api_user, user_wallet_id) == 90 assert await receiving_wallet.get_spendable_balance() == 108 val = await api_admin.send_clawback_transaction({ "wallet_id": admin_wallet_id, "fee": 11 }) await time_out_assert(15, is_transaction_in_mempool, True, user_wallet_id, api_admin, val["transaction_id"]) for i in range(0, num_blocks): await full_node_api.farm_new_transaction_block( FarmNewBlockProtocol(32 * b"\0")) await time_out_assert(15, wallet_height_at_least, True, wallet_node, 76) assert await is_transaction_confirmed(user_wallet_id, api_admin, val["transaction_id"]) assert await check_balance(api_user, user_wallet_id) == 0 final_balance = await wallet.get_confirmed_balance() assert final_balance == fund_owners_initial_balance - 129