async def push_tx(self, request: Dict) -> Optional[Dict]: if "spend_bundle" not in request: raise ValueError("Spend bundle not in request") spend_bundle = SpendBundle.from_json_dict(request["spend_bundle"]) spend_name = spend_bundle.name() if self.service.mempool_manager.get_spendbundle( spend_name) is not None: status = MempoolInclusionStatus.SUCCESS error = None else: status, error = await self.service.respond_transaction( spend_bundle, spend_name) if status != MempoolInclusionStatus.SUCCESS: if self.service.mempool_manager.get_spendbundle( spend_name) is not None: # Already in mempool status = MempoolInclusionStatus.SUCCESS error = None if status == MempoolInclusionStatus.FAILED: assert error is not None raise ValueError( f"Failed to include transaction {spend_name}, error {error.name}" ) return { "status": status.name, }
def main(): # Mnemonics can be generated using `chia keys generate_and_print`, or `chia keys generate`. The latter stored # the key in the OS keychain (unencrypted file if linux). mnemonic: str = "neither medal holiday echo link dog sleep idea turkey logic security sword save taxi chapter artwork toddler wealth local mind manual never unlock narrow" seed: bytes = mnemonic_to_seed(mnemonic, passphrase="") master_private_key: PrivateKey = AugSchemeMPL.key_gen(seed) intermediate_sk: PrivateKey = AugSchemeMPL.derive_child_sk( master_private_key, 12381) intermediate_sk = AugSchemeMPL.derive_child_sk(intermediate_sk, 8444) intermediate_sk = AugSchemeMPL.derive_child_sk(intermediate_sk, 2) print( f"Parent public key is: {intermediate_sk.get_g1()}. Please use this within `create_unsigned_tx.py`" ) # If you want to use hardened keys which are more secure against quantum computers, you need to export # The public keys # create_hardened_child_public_keys(mnemonic, 1000) try: with open("tx_3.json", "r") as f: spend_bundle_json = f.read() except Exception: print( "Error: create your transaction (spend bundle) json and put it into the json file." ) return spend_bundle_json_dict: Dict = json.loads(spend_bundle_json) spend_bundle: SpendBundle = SpendBundle.from_json_dict( spend_bundle_json_dict) sign_tx(intermediate_sk, spend_bundle, use_hardened_keys=False)
async def push_tx(self, request: Dict) -> Optional[Dict]: if "spend_bundle" not in request: raise ValueError("Spend bundle not in request") # This is for backwards compatibility since CoinSolution has been renamed to CoinSpend if "coin_solutions" in request["spend_bundle"]: request["spend_bundle"]["coin_spends"] = request[ "spend_bundle"].pop("coin_solutions") spend_bundle = SpendBundle.from_json_dict(request["spend_bundle"]) spend_name = spend_bundle.name() if self.service.mempool_manager.get_spendbundle( spend_name) is not None: status = MempoolInclusionStatus.SUCCESS error = None else: status, error = await self.service.respond_transaction( spend_bundle, spend_name) if status != MempoolInclusionStatus.SUCCESS: if self.service.mempool_manager.get_spendbundle( spend_name) is not None: # Already in mempool status = MempoolInclusionStatus.SUCCESS error = None if status == MempoolInclusionStatus.FAILED: assert error is not None raise ValueError( f"Failed to include transaction {spend_name}, error {error.name}" ) return { "status": status.name, }
def round_trip(spend_bundle: SpendBundle, **kwargs): json_dict = spend_bundle.to_json_dict(**kwargs) if kwargs.get("include_legacy_keys", True): assert "coin_solutions" in json_dict else: assert "coin_solutions" not in json_dict if kwargs.get("exclude_modern_keys", True): assert "coin_spends" not in json_dict else: assert "coin_spends" in json_dict if "coin_spends" in json_dict and "coin_solutions" in json_dict: del json_dict["coin_solutions"] sb = SpendBundle.from_json_dict(json_dict) json_dict_2 = sb.to_json_dict() sb = SpendBundle.from_json_dict(json_dict_2) json_dict_3 = sb.to_json_dict() assert json_dict_2 == json_dict_3
def test_from_json_new(self): JSON = (""" { "coin_spends": [], "aggregated_signature": "%s" } """ % NULL_SIGNATURE) spend_bundle = SpendBundle.from_json_dict(json.loads(JSON)) json_1 = json.loads(JSON) json_2 = spend_bundle.to_json_dict(include_legacy_keys=False, exclude_modern_keys=False) assert json_1 == json_2
async def test1(self, two_nodes): num_blocks = 5 test_rpc_port = uint16(21522) nodes, _ = two_nodes full_node_api_1, full_node_api_2 = nodes server_1 = full_node_api_1.full_node.server server_2 = full_node_api_2.full_node.server def stop_node_cb(): full_node_api_1._close() server_1.close_all() full_node_rpc_api = FullNodeRpcApi(full_node_api_1.full_node) config = bt.config hostname = config["self_hostname"] daemon_port = config["daemon_port"] rpc_cleanup = await start_rpc_server( full_node_rpc_api, hostname, daemon_port, test_rpc_port, stop_node_cb, bt.root_path, config, connect_to_daemon=False, ) try: client = await FullNodeRpcClient.create(self_hostname, test_rpc_port, bt.root_path, config) state = await client.get_blockchain_state() assert state["peak"] is None assert not state["sync"]["sync_mode"] assert state["difficulty"] > 0 assert state["sub_slot_iters"] > 0 blocks = bt.get_consecutive_blocks(num_blocks) blocks = bt.get_consecutive_blocks(num_blocks, block_list_input=blocks, guarantee_transaction_block=True) assert len(await client.get_unfinished_block_headers()) == 0 assert len((await client.get_block_records(0, 100))) == 0 for block in blocks: if is_overflow_block(test_constants, block.reward_chain_block.signage_point_index): finished_ss = block.finished_sub_slots[:-1] else: finished_ss = block.finished_sub_slots unf = UnfinishedBlock( finished_ss, block.reward_chain_block.get_unfinished(), block.challenge_chain_sp_proof, block.reward_chain_sp_proof, block.foliage, block.foliage_transaction_block, block.transactions_info, block.transactions_generator, [], ) await full_node_api_1.full_node.respond_unfinished_block( full_node_protocol.RespondUnfinishedBlock(unf), None ) await full_node_api_1.full_node.respond_block(full_node_protocol.RespondBlock(block), None) assert len(await client.get_unfinished_block_headers()) > 0 assert len(await client.get_all_block(0, 2)) == 2 state = await client.get_blockchain_state() block = await client.get_block(state["peak"].header_hash) assert block == blocks[-1] assert (await client.get_block(bytes([1] * 32))) is None assert (await client.get_block_record_by_height(2)).header_hash == blocks[2].header_hash assert len((await client.get_block_records(0, 100))) == num_blocks * 2 assert (await client.get_block_record_by_height(100)) is None ph = list(blocks[-1].get_included_reward_coins())[0].puzzle_hash coins = await client.get_coin_records_by_puzzle_hash(ph) print(coins) assert len(coins) >= 1 pid = list(blocks[-1].get_included_reward_coins())[0].parent_coin_info pid_2 = list(blocks[-1].get_included_reward_coins())[1].parent_coin_info coins = await client.get_coin_records_by_parent_ids([pid, pid_2]) print(coins) assert len(coins) == 2 additions, removals = await client.get_additions_and_removals(blocks[-1].header_hash) assert len(additions) >= 2 and len(removals) == 0 wallet = WalletTool(full_node_api_1.full_node.constants) wallet_receiver = WalletTool(full_node_api_1.full_node.constants, AugSchemeMPL.key_gen(std_hash(b"123123"))) ph = wallet.get_new_puzzlehash() ph_2 = wallet.get_new_puzzlehash() ph_receiver = wallet_receiver.get_new_puzzlehash() assert len(await client.get_coin_records_by_puzzle_hash(ph)) == 0 assert len(await client.get_coin_records_by_puzzle_hash(ph_receiver)) == 0 blocks = bt.get_consecutive_blocks( 2, block_list_input=blocks, guarantee_transaction_block=True, farmer_reward_puzzle_hash=ph, pool_reward_puzzle_hash=ph, ) for block in blocks[-2:]: await full_node_api_1.full_node.respond_block(full_node_protocol.RespondBlock(block)) assert len(await client.get_coin_records_by_puzzle_hash(ph)) == 2 assert len(await client.get_coin_records_by_puzzle_hash(ph_receiver)) == 0 coin_to_spend = list(blocks[-1].get_included_reward_coins())[0] spend_bundle = wallet.generate_signed_transaction(coin_to_spend.amount, ph_receiver, coin_to_spend) assert len(await client.get_all_mempool_items()) == 0 assert len(await client.get_all_mempool_tx_ids()) == 0 assert (await client.get_mempool_item_by_tx_id(spend_bundle.name())) is None await client.push_tx(spend_bundle) coin = spend_bundle.additions()[0] assert len(await client.get_all_mempool_items()) == 1 assert len(await client.get_all_mempool_tx_ids()) == 1 assert ( SpendBundle.from_json_dict(list((await client.get_all_mempool_items()).values())[0]["spend_bundle"]) == spend_bundle ) assert (await client.get_all_mempool_tx_ids())[0] == spend_bundle.name() assert ( SpendBundle.from_json_dict( (await client.get_mempool_item_by_tx_id(spend_bundle.name()))["spend_bundle"] ) == spend_bundle ) assert (await client.get_coin_record_by_name(coin.name())) is None await full_node_api_1.farm_new_transaction_block(FarmNewBlockProtocol(ph_2)) assert (await client.get_coin_record_by_name(coin.name())).coin == coin assert len(await client.get_coin_records_by_puzzle_hash(ph_receiver)) == 1 assert len(list(filter(lambda cr: not cr.spent, (await client.get_coin_records_by_puzzle_hash(ph))))) == 3 assert len(await client.get_coin_records_by_puzzle_hashes([ph_receiver, ph])) == 5 assert len(await client.get_coin_records_by_puzzle_hash(ph, False)) == 3 assert len(await client.get_coin_records_by_puzzle_hash(ph, True)) == 4 assert len(await client.get_coin_records_by_puzzle_hash(ph, True, 0, 100)) == 4 assert len(await client.get_coin_records_by_puzzle_hash(ph, True, 50, 100)) == 0 assert len(await client.get_coin_records_by_puzzle_hash(ph, True, 0, blocks[-1].height + 1)) == 2 assert len(await client.get_coin_records_by_puzzle_hash(ph, True, 0, 1)) == 0 assert len(await client.get_connections()) == 0 await client.open_connection(self_hostname, server_2._port) async def num_connections(): return len(await client.get_connections()) await time_out_assert(10, num_connections, 1) connections = await client.get_connections() assert NodeType(connections[0]["type"]) == NodeType.FULL_NODE.value assert len(await client.get_connections(NodeType.FULL_NODE)) == 1 assert len(await client.get_connections(NodeType.FARMER)) == 0 await client.close_connection(connections[0]["node_id"]) await time_out_assert(10, num_connections, 0) finally: # Checks that the RPC manages to stop the node client.close() await client.await_closed() await rpc_cleanup()
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()
def test_dont_use_both_legacy_and_modern(self): json_1 = BLANK_SPEND_BUNDLE.to_json_dict(include_legacy_keys=True, exclude_modern_keys=False) with self.assertRaises(ValueError): SpendBundle.from_json_dict(json_1)