1060 lines
43 KiB
Python
1060 lines
43 KiB
Python
import dataclasses
|
|
import json
|
|
import logging
|
|
from operator import attrgetter
|
|
from typing import Any, Dict, List, Optional, Tuple
|
|
|
|
import pytest
|
|
import pytest_asyncio
|
|
from blspy import G2Element
|
|
|
|
from chia.consensus.block_rewards import calculate_base_farmer_reward, calculate_pool_reward
|
|
from chia.consensus.coinbase import create_puzzlehash_for_pk
|
|
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.server.server import ChiaServer
|
|
from chia.simulator.full_node_simulator import FullNodeSimulator
|
|
from chia.simulator.simulator_protocol import FarmNewBlockProtocol
|
|
from chia.types.announcement import Announcement
|
|
from chia.types.blockchain_format.coin import Coin
|
|
from chia.types.blockchain_format.program import Program
|
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
from chia.types.coin_record import CoinRecord
|
|
from chia.types.coin_spend import CoinSpend
|
|
from chia.types.peer_info import PeerInfo
|
|
from chia.types.spend_bundle import SpendBundle
|
|
from chia.util.bech32m import decode_puzzle_hash, encode_puzzle_hash
|
|
from chia.util.config import lock_and_load_config, save_config
|
|
from chia.util.hash import std_hash
|
|
from chia.util.ints import uint16, uint32, uint64
|
|
from chia.wallet.cat_wallet.cat_constants import DEFAULT_CATS
|
|
from chia.wallet.cat_wallet.cat_wallet import CATWallet
|
|
from chia.wallet.derive_keys import master_sk_to_wallet_sk, master_sk_to_wallet_sk_unhardened
|
|
from chia.wallet.did_wallet.did_wallet import DIDWallet
|
|
from chia.wallet.nft_wallet.nft_wallet import NFTWallet
|
|
from chia.wallet.trading.trade_status import TradeStatus
|
|
from chia.wallet.transaction_record import TransactionRecord
|
|
from chia.wallet.transaction_sorting import SortKey
|
|
from chia.wallet.util.compute_memos import compute_memos
|
|
from chia.wallet.util.wallet_types import WalletType
|
|
from chia.wallet.wallet import Wallet
|
|
from chia.wallet.wallet_node import WalletNode
|
|
from tests.block_tools import BlockTools
|
|
from tests.pools.test_pool_rpc import wallet_is_synced
|
|
from tests.time_out_assert import time_out_assert
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
@dataclasses.dataclass
|
|
class WalletBundle:
|
|
node: WalletNode
|
|
rpc_client: WalletRpcClient
|
|
wallet: Wallet
|
|
|
|
|
|
@dataclasses.dataclass
|
|
class FullNodeBundle:
|
|
server: ChiaServer
|
|
api: FullNodeSimulator
|
|
rpc_client: FullNodeRpcClient
|
|
|
|
|
|
@dataclasses.dataclass
|
|
class WalletRpcTestEnvironment:
|
|
wallet_1: WalletBundle
|
|
wallet_2: WalletBundle
|
|
full_node: FullNodeBundle
|
|
|
|
|
|
async def farm_transaction_block(full_node_api: FullNodeSimulator, wallet_node: WalletNode):
|
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(b"\00" * 32)))
|
|
await time_out_assert(5, wallet_is_synced, True, wallet_node, full_node_api)
|
|
|
|
|
|
async def farm_transaction(full_node_api: FullNodeSimulator, wallet_node: WalletNode, spend_bundle: SpendBundle):
|
|
await time_out_assert(5, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle, spend_bundle.name())
|
|
await farm_transaction_block(full_node_api, wallet_node)
|
|
assert full_node_api.full_node.mempool_manager.get_spendbundle(spend_bundle.name()) is None
|
|
|
|
|
|
async def generate_funds(full_node_api: FullNodeSimulator, wallet_bundle: WalletBundle, num_blocks: int = 1):
|
|
wallet_id = 1
|
|
initial_balances = await wallet_bundle.rpc_client.get_wallet_balance(str(wallet_id))
|
|
ph: bytes32 = decode_puzzle_hash(await wallet_bundle.rpc_client.get_next_address(str(wallet_id), True))
|
|
generated_funds = 0
|
|
for i in range(0, num_blocks):
|
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
peak_height = full_node_api.full_node.blockchain.get_peak_height()
|
|
assert peak_height is not None
|
|
generated_funds += calculate_pool_reward(peak_height) + calculate_base_farmer_reward(peak_height)
|
|
|
|
# Farm a dummy block to confirm the created funds
|
|
await farm_transaction_block(full_node_api, wallet_bundle.node)
|
|
|
|
expected_confirmed = initial_balances["confirmed_wallet_balance"] + generated_funds
|
|
expected_unconfirmed = initial_balances["unconfirmed_wallet_balance"] + generated_funds
|
|
await time_out_assert(10, get_confirmed_balance, expected_confirmed, wallet_bundle.rpc_client, wallet_id)
|
|
await time_out_assert(10, get_unconfirmed_balance, expected_unconfirmed, wallet_bundle.rpc_client, wallet_id)
|
|
await time_out_assert(10, wallet_bundle.rpc_client.get_synced)
|
|
|
|
return generated_funds
|
|
|
|
|
|
@pytest_asyncio.fixture(scope="function", params=[True, False])
|
|
async def wallet_rpc_environment(two_wallet_nodes, request, bt: BlockTools, self_hostname):
|
|
full_node, wallets = two_wallet_nodes
|
|
full_node_api = full_node[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
|
|
|
|
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"]
|
|
|
|
if request.param:
|
|
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"] = {}
|
|
|
|
def stop_node_cb():
|
|
pass
|
|
|
|
full_node_rpc_api = FullNodeRpcApi(full_node_api.full_node)
|
|
|
|
rpc_cleanup_node, test_rpc_port_node = await start_rpc_server(
|
|
full_node_rpc_api,
|
|
hostname,
|
|
daemon_port,
|
|
uint16(0),
|
|
stop_node_cb,
|
|
bt.root_path,
|
|
config,
|
|
connect_to_daemon=False,
|
|
)
|
|
rpc_cleanup, test_rpc_port = await start_rpc_server(
|
|
wallet_rpc_api,
|
|
hostname,
|
|
daemon_port,
|
|
uint16(0),
|
|
stop_node_cb,
|
|
bt.root_path,
|
|
config,
|
|
connect_to_daemon=False,
|
|
)
|
|
rpc_cleanup_2, test_rpc_port_2 = await start_rpc_server(
|
|
wallet_rpc_api_2,
|
|
hostname,
|
|
daemon_port,
|
|
uint16(0),
|
|
stop_node_cb,
|
|
bt.root_path,
|
|
config,
|
|
connect_to_daemon=False,
|
|
)
|
|
|
|
await server_2.start_client(PeerInfo(self_hostname, uint16(full_node_server._port)), None)
|
|
await server_3.start_client(PeerInfo(self_hostname, uint16(full_node_server._port)), None)
|
|
|
|
client = await WalletRpcClient.create(hostname, test_rpc_port, bt.root_path, config)
|
|
client_2 = await WalletRpcClient.create(hostname, test_rpc_port_2, bt.root_path, config)
|
|
client_node = await FullNodeRpcClient.create(hostname, test_rpc_port_node, bt.root_path, config)
|
|
|
|
wallet_bundle_1: WalletBundle = WalletBundle(wallet_node, client, wallet)
|
|
wallet_bundle_2: WalletBundle = WalletBundle(wallet_node_2, client_2, wallet_2)
|
|
node_bundle: FullNodeBundle = FullNodeBundle(full_node_server, full_node_api, client_node)
|
|
|
|
yield WalletRpcTestEnvironment(wallet_bundle_1, wallet_bundle_2, node_bundle)
|
|
|
|
# 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()
|
|
|
|
|
|
async def create_tx_outputs(wallet: Wallet, output_args: List[Tuple[int, Optional[List[str]]]]) -> List[Dict[str, Any]]:
|
|
outputs = []
|
|
for args in output_args:
|
|
output = {"amount": uint64(args[0]), "puzzle_hash": await wallet.get_new_puzzlehash()}
|
|
if args[1] is not None:
|
|
assert len(args[1]) > 0
|
|
output["memos"] = args[1]
|
|
outputs.append(output)
|
|
return outputs
|
|
|
|
|
|
async def assert_wallet_types(client: WalletRpcClient, expected: Dict[WalletType, int]) -> None:
|
|
for wallet_type in WalletType:
|
|
wallets = await client.get_wallets(wallet_type)
|
|
wallet_count = len(wallets)
|
|
if wallet_type in expected:
|
|
assert wallet_count == expected.get(wallet_type, 0)
|
|
for wallet in wallets:
|
|
assert wallet["type"] == wallet_type.value
|
|
|
|
|
|
def assert_tx_amounts(
|
|
tx: TransactionRecord, outputs: List[Dict[str, Any]], *, amount_fee: uint64, change_expected: bool
|
|
) -> None:
|
|
assert tx.fee_amount == amount_fee
|
|
assert tx.amount == sum(output["amount"] for output in outputs)
|
|
expected_additions = len(outputs) if change_expected is None else len(outputs) + 1
|
|
assert len(tx.additions) == expected_additions
|
|
addition_amounts = [addition.amount for addition in tx.additions]
|
|
removal_amounts = [removal.amount for removal in tx.removals]
|
|
for output in outputs:
|
|
assert output["amount"] in addition_amounts
|
|
assert (sum(removal_amounts) - sum(addition_amounts)) == amount_fee
|
|
|
|
|
|
async def assert_push_tx_error(node_rpc: FullNodeRpcClient, tx: TransactionRecord):
|
|
spend_bundle = tx.spend_bundle
|
|
assert spend_bundle is not None
|
|
# check error for a ASSERT_ANNOUNCE_CONSUMED_FAILED and if the error is not there throw a value error
|
|
try:
|
|
await node_rpc.push_tx(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
|
|
|
|
|
|
async def tx_in_mempool(client: WalletRpcClient, transaction_id: bytes32):
|
|
tx = await client.get_transaction("1", transaction_id)
|
|
return tx.is_in_mempool()
|
|
|
|
|
|
async def get_confirmed_balance(client: WalletRpcClient, wallet_id: int):
|
|
return (await client.get_wallet_balance(str(wallet_id)))["confirmed_wallet_balance"]
|
|
|
|
|
|
async def get_unconfirmed_balance(client: WalletRpcClient, wallet_id: int):
|
|
return (await client.get_wallet_balance(str(wallet_id)))["unconfirmed_wallet_balance"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_send_transaction(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
|
|
wallet_2: Wallet = env.wallet_2.wallet
|
|
wallet_node: WalletNode = env.wallet_1.node
|
|
full_node_api: FullNodeSimulator = env.full_node.api
|
|
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
|
|
generated_funds = await generate_funds(full_node_api, env.wallet_1)
|
|
|
|
addr = encode_puzzle_hash(await wallet_2.get_new_puzzlehash(), "txch")
|
|
tx_amount = uint64(15600000)
|
|
with pytest.raises(ValueError):
|
|
await client.send_transaction("1", uint64(100000000000000001), addr)
|
|
|
|
# Tests sending a basic transaction
|
|
tx = await client.send_transaction("1", tx_amount, addr, memos=["this is a basic tx"])
|
|
transaction_id = tx.name
|
|
|
|
spend_bundle = tx.spend_bundle
|
|
assert spend_bundle is not None
|
|
|
|
await time_out_assert(5, tx_in_mempool, True, client, transaction_id)
|
|
await time_out_assert(5, get_unconfirmed_balance, generated_funds - tx_amount, client, 1)
|
|
|
|
await farm_transaction(full_node_api, wallet_node, spend_bundle)
|
|
|
|
# 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 spend_bundle.additions()]
|
|
|
|
await time_out_assert(5, get_confirmed_balance, generated_funds - tx_amount, client, 1)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"output_args, fee, select_coin",
|
|
[
|
|
([(348026, None)], 0, False),
|
|
([(1270495230, ["memo_1"]), (902347, ["memo_2"])], 1, True),
|
|
([(84920, ["memo_1_0", "memo_1_1"]), (1, ["memo_2_0"])], 0, False),
|
|
([(32058710, ["memo_1_0", "memo_1_1"]), (1, ["memo_2_0"]), (923, ["memo_3_0", "memo_3_1"])], 32804, True),
|
|
],
|
|
)
|
|
@pytest.mark.asyncio
|
|
async def test_create_signed_transaction(
|
|
wallet_rpc_environment: WalletRpcTestEnvironment,
|
|
output_args: List[Tuple[int, Optional[List[str]]]],
|
|
fee: int,
|
|
select_coin: bool,
|
|
):
|
|
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
|
|
wallet_2: Wallet = env.wallet_2.wallet
|
|
wallet_1_node: WalletNode = env.wallet_1.node
|
|
wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client
|
|
full_node_api: FullNodeSimulator = env.full_node.api
|
|
full_node_rpc: FullNodeRpcClient = env.full_node.rpc_client
|
|
|
|
generated_funds = await generate_funds(full_node_api, env.wallet_1)
|
|
|
|
outputs = await create_tx_outputs(wallet_2, output_args)
|
|
amount_outputs = sum(output["amount"] for output in outputs)
|
|
amount_fee = uint64(fee)
|
|
amount_total = amount_outputs + amount_fee
|
|
|
|
selected_coin = None
|
|
if select_coin:
|
|
selected_coin = await wallet_1_rpc.select_coins(amount=amount_total, wallet_id=1)
|
|
assert len(selected_coin) == 1
|
|
|
|
tx = await wallet_1_rpc.create_signed_transaction(
|
|
outputs,
|
|
coins=selected_coin,
|
|
fee=amount_fee,
|
|
)
|
|
assert_tx_amounts(tx, outputs, amount_fee=amount_fee, change_expected=not select_coin)
|
|
|
|
# Farm the transaction and make sure the wallet balance reflects it correct
|
|
spend_bundle = tx.spend_bundle
|
|
assert spend_bundle is not None
|
|
push_res = await full_node_rpc.push_tx(spend_bundle)
|
|
assert push_res["success"]
|
|
await farm_transaction(full_node_api, wallet_1_node, spend_bundle)
|
|
await time_out_assert(5, get_confirmed_balance, generated_funds - amount_total, wallet_1_rpc, 1)
|
|
|
|
# Validate the memos
|
|
for output in outputs:
|
|
if "memos" in outputs:
|
|
found: bool = False
|
|
for addition in spend_bundle.additions():
|
|
if addition.amount == output["amount"] and addition.puzzle_hash.hex() == output["puzzle_hash"]:
|
|
cr: Optional[CoinRecord] = await full_node_rpc.get_coin_record_by_name(addition.name())
|
|
assert cr is not None
|
|
spend: Optional[CoinSpend] = await full_node_rpc.get_puzzle_and_solution(
|
|
addition.parent_coin_info, cr.confirmed_block_index
|
|
)
|
|
assert spend is not None
|
|
sb: SpendBundle = SpendBundle([spend], G2Element())
|
|
assert compute_memos(sb) == {addition.name(): [memo.encode() for memo in output["memos"]]}
|
|
found = True
|
|
assert found
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_signed_transaction_with_coin_announcement(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
|
|
wallet_2: Wallet = env.wallet_2.wallet
|
|
full_node_api: FullNodeSimulator = env.full_node.api
|
|
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
client_node: FullNodeRpcClient = env.full_node.rpc_client
|
|
|
|
await generate_funds(full_node_api, env.wallet_1)
|
|
|
|
signed_tx_amount = uint64(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")),
|
|
),
|
|
]
|
|
outputs = await create_tx_outputs(wallet_2, [(signed_tx_amount, None)])
|
|
tx_res: TransactionRecord = await client.create_signed_transaction(
|
|
outputs, coin_announcements=tx_coin_announcements
|
|
)
|
|
assert_tx_amounts(tx_res, outputs, amount_fee=uint64(0), change_expected=False)
|
|
await assert_push_tx_error(client_node, tx_res)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_signed_transaction_with_puzzle_announcement(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
|
|
wallet_2: Wallet = env.wallet_2.wallet
|
|
full_node_api: FullNodeSimulator = env.full_node.api
|
|
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
client_node: FullNodeRpcClient = env.full_node.rpc_client
|
|
|
|
await generate_funds(full_node_api, env.wallet_1)
|
|
|
|
signed_tx_amount = uint64(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")),
|
|
),
|
|
]
|
|
outputs = await create_tx_outputs(wallet_2, [(signed_tx_amount, None)])
|
|
tx_res = await client.create_signed_transaction(outputs, puzzle_announcements=tx_puzzle_announcements)
|
|
assert_tx_amounts(tx_res, outputs, amount_fee=uint64(0), change_expected=True)
|
|
await assert_push_tx_error(client_node, tx_res)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_send_transaction_multi(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
|
|
wallet_2: Wallet = env.wallet_2.wallet
|
|
wallet_node: WalletNode = env.wallet_1.node
|
|
full_node_api: FullNodeSimulator = env.full_node.api
|
|
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
|
|
generated_funds = await generate_funds(full_node_api, env.wallet_1)
|
|
|
|
outputs = await create_tx_outputs(wallet_2, [(uint64(1), ["memo_1"]), (uint64(2), ["memo_2"])])
|
|
amount_outputs = sum(output["amount"] for output in outputs)
|
|
amount_fee = uint64(amount_outputs + 1)
|
|
|
|
send_tx_res: TransactionRecord = await client.send_transaction_multi(
|
|
"1",
|
|
outputs,
|
|
fee=amount_fee,
|
|
)
|
|
spend_bundle = send_tx_res.spend_bundle
|
|
assert spend_bundle is not None
|
|
assert send_tx_res is not None
|
|
|
|
assert_tx_amounts(send_tx_res, outputs, amount_fee=amount_fee, change_expected=True)
|
|
|
|
await farm_transaction(full_node_api, wallet_node, spend_bundle)
|
|
|
|
await time_out_assert(5, get_confirmed_balance, generated_funds - amount_outputs - amount_fee, client, 1)
|
|
|
|
# Checks that the memo can be retrieved
|
|
tx_confirmed = await client.get_transaction("1", send_tx_res.name)
|
|
assert tx_confirmed.confirmed
|
|
memos = tx_confirmed.get_memos()
|
|
assert len(memos) == len(outputs)
|
|
for output in outputs:
|
|
assert [output["memos"][0].encode()] in memos.values()
|
|
spend_bundle = send_tx_res.spend_bundle
|
|
assert spend_bundle is not None
|
|
for key in memos.keys():
|
|
assert key in [a.name() for a in spend_bundle.additions()]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_transactions(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
|
|
wallet: Wallet = env.wallet_1.wallet
|
|
wallet_node: WalletNode = env.wallet_1.node
|
|
full_node_api: FullNodeSimulator = env.full_node.api
|
|
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
|
|
await generate_funds(full_node_api, env.wallet_1, 5)
|
|
|
|
all_transactions = await client.get_transactions("1")
|
|
assert len(all_transactions) >= 10
|
|
# 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", uint64(1), encode_puzzle_hash(await wallet.get_new_puzzlehash(), "txch")
|
|
) # 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
|
|
|
|
# Test get_transactions to address
|
|
ph_by_addr = await wallet.get_new_puzzlehash()
|
|
await client.send_transaction("1", uint64(1), encode_puzzle_hash(ph_by_addr, "txch"))
|
|
await client.farm_block(encode_puzzle_hash(ph_by_addr, "txch"))
|
|
await time_out_assert(10, wallet_is_synced, True, wallet_node, full_node_api)
|
|
tx_for_address = await client.get_transactions("1", to_address=encode_puzzle_hash(ph_by_addr, "txch"))
|
|
assert len(tx_for_address) == 1
|
|
assert tx_for_address[0].to_puzzle_hash == ph_by_addr
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_transaction_count(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
|
|
full_node_api: FullNodeSimulator = env.full_node.api
|
|
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
|
|
await generate_funds(full_node_api, env.wallet_1)
|
|
|
|
all_transactions = await client.get_transactions("1")
|
|
assert len(all_transactions) > 0
|
|
transaction_count = await client.get_transaction_count("1")
|
|
assert transaction_count == len(all_transactions)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_cat_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
|
|
wallet_node: WalletNode = env.wallet_1.node
|
|
|
|
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
client_2: WalletRpcClient = env.wallet_2.rpc_client
|
|
|
|
full_node_api: FullNodeSimulator = env.full_node.api
|
|
|
|
await generate_funds(full_node_api, env.wallet_1, 1)
|
|
await generate_funds(full_node_api, env.wallet_2, 1)
|
|
|
|
# Creates a CAT wallet with 100 mojos and a CAT with 20 mojos
|
|
await client.create_new_cat_and_wallet(uint64(100))
|
|
res = await client.create_new_cat_and_wallet(uint64(20))
|
|
assert res["success"]
|
|
cat_0_id = res["wallet_id"]
|
|
asset_id = bytes32.fromhex(res["asset_id"])
|
|
assert len(asset_id) > 0
|
|
|
|
await assert_wallet_types(client, {WalletType.STANDARD_WALLET: 1, WalletType.CAT: 2})
|
|
await assert_wallet_types(client_2, {WalletType.STANDARD_WALLET: 1})
|
|
|
|
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)) == CATWallet.default_wallet_name_for_unknown_cat(asset_id.hex())
|
|
await client.set_cat_name(cat_0_id, "My cat")
|
|
assert (await client.get_cat_name(cat_0_id)) == "My cat"
|
|
result = await client.cat_asset_id_to_name(col)
|
|
assert result is not None
|
|
wid, name = result
|
|
assert wid == cat_0_id
|
|
assert name == "My cat"
|
|
result = await client.cat_asset_id_to_name(bytes32([0] * 32))
|
|
assert result is None
|
|
verified_asset_id = next(iter(DEFAULT_CATS.items()))[1]["asset_id"]
|
|
result = await client.cat_asset_id_to_name(bytes32.from_hexstr(verified_asset_id))
|
|
assert result is not None
|
|
should_be_none, name = result
|
|
assert should_be_none is None
|
|
assert name == next(iter(DEFAULT_CATS.items()))[1]["name"]
|
|
|
|
# TODO: Investigate why farming only one block here makes it flaky
|
|
await farm_transaction_block(full_node_api, wallet_node)
|
|
await farm_transaction_block(full_node_api, wallet_node)
|
|
|
|
await time_out_assert(10, get_confirmed_balance, 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"]
|
|
cat_1_asset_id = bytes.fromhex(res["asset_id"])
|
|
assert cat_1_asset_id == asset_id
|
|
|
|
await assert_wallet_types(client, {WalletType.STANDARD_WALLET: 1, WalletType.CAT: 2})
|
|
await assert_wallet_types(client_2, {WalletType.STANDARD_WALLET: 1, WalletType.CAT: 1})
|
|
|
|
await farm_transaction_block(full_node_api, wallet_node)
|
|
|
|
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
|
|
|
|
tx_res = await client.cat_spend(cat_0_id, uint64(4), addr_1, uint64(0), ["the cat memo"])
|
|
spend_bundle = tx_res.spend_bundle
|
|
assert spend_bundle is not None
|
|
await farm_transaction(full_node_api, wallet_node, spend_bundle)
|
|
|
|
# Test unacknowledged CAT
|
|
assert wallet_node.wallet_state_manager is not None
|
|
await wallet_node.wallet_state_manager.interested_store.add_unacknowledged_token(
|
|
asset_id, "Unknown", uint32(10000), bytes32(b"\00" * 32)
|
|
)
|
|
cats = await client.get_stray_cats()
|
|
assert len(cats) == 1
|
|
|
|
await time_out_assert(10, get_confirmed_balance, 16, client, cat_0_id)
|
|
await time_out_assert(10, get_confirmed_balance, 4, client_2, cat_1_id)
|
|
|
|
# Test CAT coin selection
|
|
selected_coins = await client.select_coins(amount=1, wallet_id=cat_0_id)
|
|
assert len(selected_coins) > 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_offer_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
|
|
wallet_node: WalletNode = env.wallet_1.node
|
|
wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client
|
|
wallet_2_rpc: WalletRpcClient = env.wallet_2.rpc_client
|
|
full_node_api: FullNodeSimulator = env.full_node.api
|
|
|
|
await generate_funds(full_node_api, env.wallet_1, 1)
|
|
await generate_funds(full_node_api, env.wallet_2, 1)
|
|
|
|
# Creates a CAT wallet with 20 mojos
|
|
res = await wallet_1_rpc.create_new_cat_and_wallet(uint64(20))
|
|
cat_wallet_id = res["wallet_id"]
|
|
cat_asset_id = bytes32.fromhex(res["asset_id"])
|
|
# TODO: Investigate why farming only two blocks here makes it flaky
|
|
await farm_transaction_block(full_node_api, wallet_node)
|
|
await farm_transaction_block(full_node_api, wallet_node)
|
|
await farm_transaction_block(full_node_api, wallet_node)
|
|
await time_out_assert(10, get_confirmed_balance, 20, wallet_1_rpc, cat_wallet_id)
|
|
|
|
# Creates a wallet for the same CAT on wallet_2 and send 4 CAT from wallet_1 to it
|
|
await wallet_2_rpc.create_wallet_for_existing_cat(cat_asset_id)
|
|
wallet_2_address = await wallet_2_rpc.get_next_address(cat_wallet_id, False)
|
|
tx_res = await wallet_1_rpc.cat_spend(cat_wallet_id, uint64(4), wallet_2_address, uint64(0), ["the cat memo"])
|
|
spend_bundle = tx_res.spend_bundle
|
|
assert spend_bundle is not None
|
|
await farm_transaction(full_node_api, wallet_node, spend_bundle)
|
|
await time_out_assert(10, get_confirmed_balance, 4, wallet_2_rpc, cat_wallet_id)
|
|
|
|
# Create an offer of 5 chia for one CAT
|
|
offer, trade_record = await wallet_1_rpc.create_offer_for_ids(
|
|
{uint32(1): -5, cat_asset_id.hex(): 1}, validate_only=True
|
|
)
|
|
all_offers = await wallet_1_rpc.get_all_offers()
|
|
assert len(all_offers) == 0
|
|
assert offer is None
|
|
|
|
driver_dict: Dict[str, Any] = {cat_asset_id.hex(): {"type": "CAT", "tail": "0x" + cat_asset_id.hex()}}
|
|
|
|
offer, trade_record = await wallet_1_rpc.create_offer_for_ids(
|
|
{uint32(1): -5, cat_asset_id.hex(): 1},
|
|
driver_dict=driver_dict,
|
|
fee=uint64(1),
|
|
)
|
|
assert offer is not None
|
|
|
|
summary = await wallet_1_rpc.get_offer_summary(offer)
|
|
assert summary == {"offered": {"xch": 5}, "requested": {cat_asset_id.hex(): 1}, "infos": driver_dict, "fees": 1}
|
|
|
|
assert await wallet_1_rpc.check_offer_validity(offer)
|
|
|
|
all_offers = await wallet_1_rpc.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 wallet_2_rpc.take_offer(offer, fee=uint64(1))
|
|
assert TradeStatus(trade_record.status) == TradeStatus.PENDING_CONFIRM
|
|
|
|
await wallet_1_rpc.cancel_offer(offer.name(), secure=False)
|
|
|
|
trade_record = await wallet_1_rpc.get_offer(offer.name(), file_contents=True)
|
|
assert trade_record.offer == bytes(offer)
|
|
assert TradeStatus(trade_record.status) == TradeStatus.CANCELLED
|
|
|
|
await wallet_1_rpc.cancel_offer(offer.name(), fee=uint64(1), secure=True)
|
|
|
|
trade_record = await wallet_1_rpc.get_offer(offer.name())
|
|
assert TradeStatus(trade_record.status) == TradeStatus.PENDING_CANCEL
|
|
|
|
new_offer, new_trade_record = await wallet_1_rpc.create_offer_for_ids(
|
|
{uint32(1): -5, cat_wallet_id: 1}, fee=uint64(1)
|
|
)
|
|
all_offers = await wallet_1_rpc.get_all_offers()
|
|
assert len(all_offers) == 2
|
|
|
|
await farm_transaction_block(full_node_api, wallet_node)
|
|
|
|
async def is_trade_confirmed(client, trade) -> bool:
|
|
trade_record = await client.get_offer(trade.name())
|
|
return TradeStatus(trade_record.status) == TradeStatus.CONFIRMED
|
|
|
|
await time_out_assert(15, is_trade_confirmed, True, wallet_1_rpc, offer)
|
|
|
|
# Test trade sorting
|
|
def only_ids(trades):
|
|
return [t.trade_id for t in trades]
|
|
|
|
trade_record = await wallet_1_rpc.get_offer(offer.name())
|
|
all_offers = await wallet_1_rpc.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 wallet_1_rpc.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 wallet_1_rpc.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 wallet_1_rpc.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 wallet_1_rpc.get_all_offers(include_completed=True, start=0, end=1)
|
|
assert len(all_offers) == 1
|
|
all_offers = await wallet_1_rpc.get_all_offers(include_completed=True, start=50)
|
|
assert len(all_offers) == 0
|
|
all_offers = await wallet_1_rpc.get_all_offers(include_completed=True, start=0, end=50)
|
|
assert len(all_offers) == 2
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_did_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
from chia.wallet.did_wallet.did_info import DID_HRP
|
|
|
|
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
|
|
wallet_1: Wallet = env.wallet_1.wallet
|
|
wallet_2: Wallet = env.wallet_2.wallet
|
|
wallet_1_node: WalletNode = env.wallet_1.node
|
|
wallet_2_node: WalletNode = env.wallet_2.node
|
|
wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client
|
|
full_node_api: FullNodeSimulator = env.full_node.api
|
|
wallet_1_id = wallet_1.id()
|
|
|
|
await generate_funds(env.full_node.api, env.wallet_1, 5)
|
|
|
|
# Create a DID wallet
|
|
res = await wallet_1_rpc.create_new_did_wallet(amount=1, name=None)
|
|
assert res["success"]
|
|
did_wallet_id_0 = res["wallet_id"]
|
|
did_id_0 = res["my_did"]
|
|
|
|
# Get wallet name
|
|
res = await wallet_1_rpc.did_get_wallet_name(did_wallet_id_0)
|
|
assert res["success"]
|
|
assert res["name"] == "Profile 1"
|
|
|
|
# Set wallet name
|
|
new_wallet_name = "test name"
|
|
res = await wallet_1_rpc.did_set_wallet_name(did_wallet_id_0, new_wallet_name)
|
|
assert res["success"]
|
|
res = await wallet_1_rpc.did_get_wallet_name(did_wallet_id_0)
|
|
assert res["success"]
|
|
assert res["name"] == new_wallet_name
|
|
with pytest.raises(ValueError, match="Wallet id 1 is not a DID wallet"):
|
|
await wallet_1_rpc.did_set_wallet_name(wallet_1_id, new_wallet_name)
|
|
|
|
# Check DID ID
|
|
res = await wallet_1_rpc.get_did_id(did_wallet_id_0)
|
|
assert res["success"]
|
|
assert did_id_0 == res["my_did"]
|
|
# Create backup file
|
|
res = await wallet_1_rpc.create_did_backup_file(did_wallet_id_0, "backup.did")
|
|
assert res["success"]
|
|
|
|
for _ in range(3):
|
|
await farm_transaction_block(full_node_api, wallet_1_node)
|
|
|
|
# Update recovery list
|
|
res = await wallet_1_rpc.update_did_recovery_list(did_wallet_id_0, [did_id_0], 1)
|
|
assert res["success"]
|
|
res = await wallet_1_rpc.get_did_recovery_list(did_wallet_id_0)
|
|
assert res["num_required"] == 1
|
|
assert res["recovery_list"][0] == did_id_0
|
|
|
|
for _ in range(3):
|
|
await farm_transaction_block(full_node_api, wallet_1_node)
|
|
|
|
# Update metadata
|
|
with pytest.raises(ValueError, match="Wallet with id 1 is not a DID one"):
|
|
await wallet_1_rpc.update_did_metadata(wallet_1_id, {"Twitter": "Https://test"})
|
|
res = await wallet_1_rpc.update_did_metadata(did_wallet_id_0, {"Twitter": "Https://test"})
|
|
assert res["success"]
|
|
|
|
for _ in range(3):
|
|
await farm_transaction_block(full_node_api, wallet_1_node)
|
|
|
|
res = await wallet_1_rpc.get_did_metadata(did_wallet_id_0)
|
|
assert res["metadata"]["Twitter"] == "Https://test"
|
|
|
|
for _ in range(3):
|
|
await farm_transaction_block(full_node_api, wallet_1_node)
|
|
|
|
# Transfer DID
|
|
addr = encode_puzzle_hash(await wallet_2.get_new_puzzlehash(), "txch")
|
|
res = await wallet_1_rpc.did_transfer_did(did_wallet_id_0, addr, 0, True)
|
|
assert res["success"]
|
|
|
|
for _ in range(3):
|
|
await farm_transaction_block(full_node_api, wallet_1_node)
|
|
|
|
assert wallet_2_node.wallet_state_manager is not None
|
|
|
|
did_wallets = list(
|
|
filter(
|
|
lambda w: (w.type == WalletType.DECENTRALIZED_ID),
|
|
await wallet_2_node.wallet_state_manager.get_all_wallet_info_entries(),
|
|
)
|
|
)
|
|
did_wallet_2: DIDWallet = wallet_2_node.wallet_state_manager.wallets[did_wallets[0].id]
|
|
assert encode_puzzle_hash(bytes32.from_hexstr(did_wallet_2.get_my_DID()), DID_HRP) == did_id_0
|
|
metadata = json.loads(did_wallet_2.did_info.metadata)
|
|
assert metadata["Twitter"] == "Https://test"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_nft_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
|
|
from chia.wallet.nft_wallet.nft_info import NFT_HRP
|
|
|
|
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
wallet_1_node: WalletNode = env.wallet_1.node
|
|
wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client
|
|
wallet_2: Wallet = env.wallet_2.wallet
|
|
wallet_2_node: WalletNode = env.wallet_2.node
|
|
wallet_2_rpc: WalletRpcClient = env.wallet_2.rpc_client
|
|
full_node_api: FullNodeSimulator = env.full_node.api
|
|
|
|
await generate_funds(env.full_node.api, env.wallet_1, 5)
|
|
|
|
res = await wallet_1_rpc.create_new_nft_wallet(None)
|
|
nft_wallet_id = res["wallet_id"]
|
|
res = await wallet_1_rpc.mint_nft(
|
|
nft_wallet_id,
|
|
None,
|
|
None,
|
|
"0xD4584AD463139FA8C0D9F68F4B59F185",
|
|
["https://www.chia.net/img/branding/chia-logo.svg"],
|
|
)
|
|
assert res["success"]
|
|
|
|
for _ in range(3):
|
|
await farm_transaction_block(full_node_api, wallet_1_node)
|
|
|
|
assert wallet_1_node.wallet_state_manager is not None
|
|
|
|
nft_wallet: NFTWallet = wallet_1_node.wallet_state_manager.wallets[nft_wallet_id]
|
|
# Test with the hex version of nft_id
|
|
nft_id = nft_wallet.get_current_nfts()[0].coin.name().hex()
|
|
nft_info = (await wallet_1_rpc.get_nft_info(nft_id))["nft_info"]
|
|
assert nft_info["nft_coin_id"][2:] == nft_wallet.get_current_nfts()[0].coin.name().hex()
|
|
# Test with the bech32m version of nft_id
|
|
hmr_nft_id = encode_puzzle_hash(nft_wallet.get_current_nfts()[0].coin.name(), NFT_HRP)
|
|
nft_info = (await wallet_1_rpc.get_nft_info(hmr_nft_id))["nft_info"]
|
|
assert nft_info["nft_coin_id"][2:] == nft_wallet.get_current_nfts()[0].coin.name().hex()
|
|
|
|
addr = encode_puzzle_hash(await wallet_2.get_new_puzzlehash(), "txch")
|
|
res = await wallet_1_rpc.transfer_nft(nft_wallet_id, nft_id, addr, 0)
|
|
assert res["success"]
|
|
|
|
for _ in range(3):
|
|
await farm_transaction_block(full_node_api, wallet_1_node)
|
|
|
|
assert wallet_2_node.wallet_state_manager is not None
|
|
|
|
nft_wallet_id_1 = (
|
|
await wallet_2_node.wallet_state_manager.get_all_wallet_info_entries(wallet_type=WalletType.NFT)
|
|
)[0].id
|
|
nft_wallet_1: NFTWallet = wallet_2_node.wallet_state_manager.wallets[nft_wallet_id_1]
|
|
nft_info_1 = (await wallet_1_rpc.get_nft_info(nft_id, False))["nft_info"]
|
|
assert nft_info_1 == nft_info
|
|
nft_info_1 = (await wallet_1_rpc.get_nft_info(nft_id))["nft_info"]
|
|
assert nft_info_1["nft_coin_id"][2:] == nft_wallet_1.get_current_nfts()[0].coin.name().hex()
|
|
# Cross-check NFT
|
|
nft_info_2 = (await wallet_2_rpc.list_nfts(nft_wallet_id_1))["nft_list"][0]
|
|
assert nft_info_1 == nft_info_2
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_key_and_address_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
|
|
wallet: Wallet = env.wallet_1.wallet
|
|
wallet_node: WalletNode = env.wallet_1.node
|
|
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
|
|
address = await client.get_next_address("1", True)
|
|
assert len(address) > 10
|
|
|
|
pks = await client.get_public_keys()
|
|
assert len(pks) == 1
|
|
|
|
await generate_funds(env.full_node.api, env.wallet_1)
|
|
|
|
assert (await client.get_height_info()) > 0
|
|
|
|
ph = await wallet.get_new_puzzlehash()
|
|
addr = encode_puzzle_hash(ph, "txch")
|
|
tx_amount = uint64(15600000)
|
|
|
|
created_tx = await client.send_transaction("1", tx_amount, addr)
|
|
|
|
await time_out_assert(5, tx_in_mempool, True, client, created_tx.name)
|
|
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(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())
|
|
with lock_and_load_config(wallet_node.root_path, "config.yaml") as test_config:
|
|
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, 10)
|
|
assert sk_dict["fingerprint"] == 123456
|
|
assert sk_dict["used_for_farmer_rewards"] is False
|
|
assert sk_dict["used_for_pool_rewards"] is False
|
|
|
|
# Add in observer 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_unhardened(sk, uint32(0)).get_g1())
|
|
with lock_and_load_config(wallet_node.root_path, "config.yaml") as test_config:
|
|
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_unhardened(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, 10)
|
|
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(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
|
|
assert await get_unconfirmed_balance(client, int(wallets[0]["id"])) == 0
|
|
|
|
with pytest.raises(ValueError):
|
|
await client.send_transaction(wallets[0]["id"], uint64(100), addr)
|
|
|
|
# Delete all keys
|
|
await client.delete_all_keys()
|
|
assert len(await client.get_public_keys()) == 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_select_coins_rpc(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
|
|
wallet_2: Wallet = env.wallet_2.wallet
|
|
wallet_node: WalletNode = env.wallet_1.node
|
|
full_node_api: FullNodeSimulator = env.full_node.api
|
|
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
client_2: WalletRpcClient = env.wallet_2.rpc_client
|
|
|
|
funds = await generate_funds(full_node_api, env.wallet_1)
|
|
|
|
addr = encode_puzzle_hash(await wallet_2.get_new_puzzlehash(), "txch")
|
|
coin_300: List[Coin]
|
|
for tx_amount in [uint64(1000), uint64(300), uint64(1000), uint64(1000), uint64(10000)]:
|
|
funds -= tx_amount
|
|
# create coins for tests
|
|
tx = await client.send_transaction("1", tx_amount, addr)
|
|
spend_bundle = tx.spend_bundle
|
|
assert spend_bundle is not None
|
|
for coin in spend_bundle.additions():
|
|
if coin.amount == uint64(300):
|
|
coin_300 = [coin]
|
|
|
|
await time_out_assert(5, tx_in_mempool, True, client, tx.name)
|
|
await farm_transaction(full_node_api, wallet_node, spend_bundle)
|
|
await time_out_assert(5, get_confirmed_balance, funds, client, 1)
|
|
|
|
# test min coin amount
|
|
min_coins: List[Coin] = await client_2.select_coins(amount=1000, wallet_id=1, min_coin_amount=uint64(1001))
|
|
assert min_coins is not None
|
|
assert len(min_coins) == 1 and min_coins[0].amount == uint64(10000)
|
|
|
|
# test excluded coins
|
|
with pytest.raises(ValueError):
|
|
await client_2.select_coins(amount=5000, wallet_id=1, excluded_coins=min_coins)
|
|
excluded_test = await client_2.select_coins(amount=1300, wallet_id=1, excluded_coins=coin_300)
|
|
assert len(excluded_test) == 2
|
|
for coin in excluded_test:
|
|
assert coin != coin_300[0]
|