251 lines
10 KiB
Python
251 lines
10 KiB
Python
import asyncio
|
|
import logging
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from chia.consensus.block_rewards import calculate_base_farmer_reward, calculate_pool_reward
|
|
from chia.rpc.full_node_rpc_api import FullNodeRpcApi
|
|
from chia.rpc.full_node_rpc_client import FullNodeRpcClient
|
|
from chia.rpc.rpc_server import start_rpc_server
|
|
from chia.rpc.wallet_rpc_api import WalletRpcApi
|
|
from chia.rpc.wallet_rpc_client import WalletRpcClient
|
|
from chia.simulator.simulator_protocol import FarmNewBlockProtocol
|
|
from chia.types.blockchain_format.coin import Coin
|
|
from chia.types.peer_info import PeerInfo
|
|
from chia.types.spend_bundle import SpendBundle
|
|
from chia.util.bech32m import encode_puzzle_hash
|
|
from chia.util.ints import uint16, uint32
|
|
from tests.setup_nodes import bt, setup_simulators_and_wallets, self_hostname
|
|
from tests.time_out_assert import time_out_assert
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class TestWalletRpc:
|
|
@pytest.fixture(scope="function")
|
|
async def two_wallet_nodes(self):
|
|
async for _ in setup_simulators_and_wallets(1, 2, {}):
|
|
yield _
|
|
|
|
@pytest.mark.asyncio
|
|
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()
|