mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-08-29 18:14:26 -05:00
* context manager for BlockTools * Missed one usage and fixed up Self import * Ignore cleanup errors in tempdir cleanup for BlockTools * Remove debug prints * Fixed up sync blocktools and use @contextmanager * Address review comments * Add return type * Empty commit for github * Strange github things * adjust order of finding ports
522 lines
24 KiB
Python
522 lines
24 KiB
Python
from __future__ import annotations
|
|
|
|
import importlib.metadata
|
|
import json
|
|
from collections.abc import AsyncIterator
|
|
|
|
import aiohttp
|
|
import dns.rdataclass
|
|
import dns.rdatatype
|
|
import dns.rdtypes.IN.A
|
|
import dns.rdtypes.IN.AAAA
|
|
import pytest
|
|
from chia_rs import BlockRecord
|
|
from chia_rs.sized_bytes import bytes32
|
|
from chia_rs.sized_ints import uint16, uint32, uint64
|
|
|
|
from chia._tests.conftest import test_constants_modified
|
|
from chia._tests.core.node_height import node_height_at_least
|
|
from chia._tests.util.setup_nodes import FullSystem, OldSimulatorsAndWallets
|
|
from chia._tests.util.time_out_assert import time_out_assert
|
|
from chia.cmds.units import units
|
|
from chia.consensus.block_rewards import calculate_base_farmer_reward, calculate_pool_reward
|
|
from chia.daemon.server import WebSocketServer
|
|
from chia.full_node.full_node import FullNode
|
|
from chia.full_node.full_node_api import FullNodeAPI
|
|
from chia.protocols.outbound_message import NodeType
|
|
from chia.server.server import ChiaServer
|
|
from chia.simulator.block_tools import BlockTools, create_block_tools_async
|
|
from chia.simulator.full_node_simulator import FullNodeSimulator
|
|
from chia.simulator.keyring import TempKeyring
|
|
from chia.simulator.setup_services import setup_full_node
|
|
from chia.simulator.simulator_protocol import FarmNewBlockProtocol, GetAllCoinsProtocol, ReorgProtocol
|
|
from chia.types.peer_info import PeerInfo
|
|
from chia.util.ws_message import create_payload
|
|
from chia.wallet.util.tx_config import DEFAULT_TX_CONFIG
|
|
from chia.wallet.wallet_node import WalletNode
|
|
|
|
chiapos_version = importlib.metadata.version("chiapos")
|
|
|
|
|
|
# TODO: Ideally, the db_version should be the (parameterized) db_version
|
|
# fixture, to test all versions of the database schema. This doesn't work
|
|
# because of a hack in shutting down the full node, which means you cannot run
|
|
# more than one simulations per process.
|
|
@pytest.fixture(scope="function")
|
|
async def extra_node(self_hostname) -> AsyncIterator[FullNodeAPI | FullNodeSimulator]:
|
|
with TempKeyring() as keychain:
|
|
async with create_block_tools_async(constants=test_constants_modified, keychain=keychain) as b_tools:
|
|
async with setup_full_node(
|
|
test_constants_modified,
|
|
"blockchain_test_3.db",
|
|
self_hostname,
|
|
b_tools,
|
|
db_version=2,
|
|
) as service:
|
|
yield service._api
|
|
|
|
|
|
class FakeDNSResolver:
|
|
async def resolve(self, qname, rdtype, lifetime):
|
|
if rdtype == "A":
|
|
record = dns.rdtypes.IN.A.A(dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.4")
|
|
return [record]
|
|
elif rdtype == "AAAA":
|
|
record = dns.rdtypes.IN.AAAA.AAAA(dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
|
|
return [record]
|
|
return []
|
|
|
|
|
|
class TestSimulation:
|
|
@pytest.mark.limit_consensus_modes(reason="This test only supports one running at a time.")
|
|
@pytest.mark.anyio
|
|
async def test_full_system(self, simulation, extra_node, self_hostname):
|
|
full_system: FullSystem
|
|
bt: BlockTools
|
|
full_system, bt = simulation
|
|
server1: ChiaServer = full_system.node_1._server
|
|
blocks_to_farm = 3 # farming 3 blocks is sufficient to test the system
|
|
node1_port: uint16 = server1.get_port()
|
|
node2_port: uint16 = full_system.node_2._server.get_port()
|
|
|
|
# Connect node 1 to node 2
|
|
connected: bool = await server1.start_client(PeerInfo(self_hostname, node2_port))
|
|
assert connected, f"node1 was unable to connect to node2 on port {node2_port}"
|
|
assert len(server1.get_connections(NodeType.FULL_NODE, outbound=True)) >= 1
|
|
|
|
# Connect node3 to node1 and node2 - checks come later
|
|
node3: FullNodeAPI = extra_node
|
|
server3: ChiaServer = node3.full_node.server
|
|
connected = await server3.start_client(PeerInfo(self_hostname, node1_port))
|
|
assert connected, f"server3 was unable to connect to node1 on port {node1_port}"
|
|
connected = await server3.start_client(PeerInfo(self_hostname, node2_port))
|
|
assert connected, f"server3 was unable to connect to node2 on port {node2_port}"
|
|
assert len(server3.get_connections(NodeType.FULL_NODE, outbound=True)) >= 2
|
|
|
|
# wait up to 25 mins for node2 to sync the chain to blocks_to_farm height
|
|
await time_out_assert(1500, node_height_at_least, True, full_system.node_2._api, blocks_to_farm)
|
|
|
|
async def has_compact(node1: FullNode, node2: FullNode) -> bool:
|
|
peak_height_1 = node1.blockchain.get_peak_height()
|
|
if peak_height_1 is None:
|
|
return False
|
|
headers_1 = await node1.blockchain.get_header_blocks_in_range(0, peak_height_1 - blocks_to_farm - 1)
|
|
peak_height_2 = node2.blockchain.get_peak_height()
|
|
if peak_height_2 is None:
|
|
return False
|
|
headers_2 = await node2.blockchain.get_header_blocks_in_range(0, peak_height_2 - blocks_to_farm - 1)
|
|
# Commented to speed up.
|
|
# cc_eos = [False, False]
|
|
# icc_eos = [False, False]
|
|
# cc_sp = [False, False]
|
|
# cc_ip = [False, False]
|
|
has_compact = [False, False]
|
|
for index, headers in enumerate([headers_1, headers_2]):
|
|
for header in headers.values():
|
|
for sub_slot in header.finished_sub_slots:
|
|
if sub_slot.proofs.challenge_chain_slot_proof.normalized_to_identity:
|
|
# cc_eos[index] = True
|
|
has_compact[index] = True
|
|
if (
|
|
sub_slot.proofs.infused_challenge_chain_slot_proof is not None
|
|
and sub_slot.proofs.infused_challenge_chain_slot_proof.normalized_to_identity
|
|
):
|
|
# icc_eos[index] = True
|
|
has_compact[index] = True
|
|
if (
|
|
header.challenge_chain_sp_proof is not None
|
|
and header.challenge_chain_sp_proof.normalized_to_identity
|
|
):
|
|
# cc_sp[index] = True
|
|
has_compact[index] = True
|
|
if header.challenge_chain_ip_proof.normalized_to_identity:
|
|
# cc_ip[index] = True
|
|
has_compact[index] = True
|
|
|
|
# return (
|
|
# cc_eos == [True, True] and icc_eos == [True, True] and cc_sp == [True, True] and cc_ip == [True, True]
|
|
# )
|
|
return has_compact == [True, True]
|
|
|
|
await time_out_assert(600, has_compact, True, full_system.node_1._node, full_system.node_2._node)
|
|
|
|
# check node3 has synced to the proper height
|
|
peak_height: uint32 = max(
|
|
full_system.node_1._node.blockchain.get_peak_height(),
|
|
full_system.node_2._node.blockchain.get_peak_height(),
|
|
)
|
|
# wait up to 10 mins for node3 to sync
|
|
await time_out_assert(600, node_height_at_least, True, node3, peak_height)
|
|
|
|
# Connect node_1 up to the daemon
|
|
full_system.node_1.rpc_server.connect_to_daemon(
|
|
self_hostname=self_hostname, daemon_port=full_system.daemon.daemon_port
|
|
)
|
|
|
|
async def verify_daemon_connection(daemon: WebSocketServer, service: str) -> bool:
|
|
return len(daemon.connections.get(service, set())) >= 1
|
|
|
|
await time_out_assert(60, verify_daemon_connection, True, full_system.daemon, "chia_full_node")
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
ws = await session.ws_connect(
|
|
f"wss://127.0.0.1:{full_system.daemon.daemon_port}",
|
|
autoclose=True,
|
|
autoping=True,
|
|
ssl=bt.get_daemon_ssl_context(),
|
|
max_msg_size=100 * 1024 * 1024,
|
|
)
|
|
service_name = "test_service_name"
|
|
payload = create_payload("register_service", {"service": service_name}, service_name, "daemon")
|
|
await ws.send_str(payload)
|
|
await ws.receive()
|
|
await time_out_assert(10, verify_daemon_connection, True, full_system.daemon, service_name)
|
|
|
|
blockchain_state_found = False
|
|
payload = create_payload("get_blockchain_state", {}, service_name, "chia_full_node")
|
|
await ws.send_str(payload)
|
|
msg = await ws.receive()
|
|
if msg.type == aiohttp.WSMsgType.TEXT:
|
|
message = msg.data.strip()
|
|
message = json.loads(message)
|
|
if message["command"] == "get_blockchain_state":
|
|
blockchain_state_found = True
|
|
|
|
await ws.close()
|
|
|
|
assert blockchain_state_found, "Could not get blockchain state from daemon and node"
|
|
|
|
@pytest.mark.anyio
|
|
async def test_simulator_auto_farm_and_get_coins(
|
|
self,
|
|
two_wallet_nodes: tuple[list[FullNodeSimulator], list[tuple[WalletNode, ChiaServer]], BlockTools],
|
|
self_hostname: str,
|
|
) -> None:
|
|
num_blocks = 2
|
|
full_nodes, wallets, _ = two_wallet_nodes
|
|
full_node_api = full_nodes[0]
|
|
server_1 = 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
|
|
async with wallet.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope:
|
|
ph = await action_scope.get_puzzle_hash(wallet.wallet_state_manager)
|
|
wallet_node.config["trusted_peers"] = {}
|
|
wallet_node_2.config["trusted_peers"] = {}
|
|
|
|
# enable auto_farming
|
|
await full_node_api.update_autofarm_config(True)
|
|
|
|
await server_2.start_client(PeerInfo(self_hostname, server_1.get_port()), None)
|
|
for i in range(num_blocks):
|
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
|
|
block_reward = calculate_pool_reward(uint32(1)) + calculate_base_farmer_reward(uint32(1))
|
|
funds = block_reward
|
|
|
|
await time_out_assert(10, wallet.get_confirmed_balance, funds)
|
|
await time_out_assert(5, wallet.get_unconfirmed_balance, funds)
|
|
async with wallet_2.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope:
|
|
ph_2 = await action_scope.get_puzzle_hash(wallet_2.wallet_state_manager)
|
|
async with wallet.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope:
|
|
await wallet.generate_signed_transaction(
|
|
[uint64(10)],
|
|
[ph_2],
|
|
action_scope,
|
|
uint64(0),
|
|
)
|
|
[tx] = await wallet.wallet_state_manager.add_pending_transactions(action_scope.side_effects.transactions)
|
|
# wait till out of mempool
|
|
await time_out_assert(10, full_node_api.full_node.mempool_manager.get_spendbundle, None, tx.name)
|
|
# wait until the transaction is confirmed
|
|
await time_out_assert(20, wallet_node.wallet_state_manager.blockchain.get_finished_sync_up_to, 3)
|
|
funds += block_reward # add auto farmed block.
|
|
await time_out_assert(10, wallet.get_confirmed_balance, funds - 10)
|
|
|
|
for i in range(num_blocks):
|
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
funds += block_reward
|
|
# to reduce test flake, check block height again
|
|
await time_out_assert(30, wallet_node.wallet_state_manager.blockchain.get_finished_sync_up_to, 5)
|
|
await time_out_assert(10, wallet.get_confirmed_balance, funds - 10)
|
|
await time_out_assert(5, wallet.get_unconfirmed_balance, funds - 10)
|
|
# now lets test getting all coins, first only unspent, then all
|
|
# we do this here, because we have a wallet.
|
|
non_spent_coins = await full_node_api.get_all_coins(GetAllCoinsProtocol(False))
|
|
assert len(non_spent_coins) == 11
|
|
spent_and_non_spent_coins = await full_node_api.get_all_coins(GetAllCoinsProtocol(True))
|
|
assert len(spent_and_non_spent_coins) == 12
|
|
# try reorg, then check again.
|
|
# revert to height 2, then go to height 6, so that we don't include the transaction we made.
|
|
await full_node_api.reorg_from_index_to_new_index(ReorgProtocol(uint32(2), uint32(6), ph, None))
|
|
reorg_non_spent_coins = await full_node_api.get_all_coins(GetAllCoinsProtocol(False))
|
|
reorg_spent_and_non_spent_coins = await full_node_api.get_all_coins(GetAllCoinsProtocol(True))
|
|
assert len(reorg_non_spent_coins) == 12 and len(reorg_spent_and_non_spent_coins) == 12
|
|
assert tx.additions not in spent_and_non_spent_coins # just double check that those got reverted.
|
|
|
|
@pytest.mark.anyio
|
|
@pytest.mark.parametrize(argnames="count", argvalues=[0, 1, 2, 5, 10])
|
|
async def test_simulation_farm_blocks_to_puzzlehash(
|
|
self,
|
|
count,
|
|
simulator_and_wallet: OldSimulatorsAndWallets,
|
|
):
|
|
[[full_node_api], _, _] = simulator_and_wallet
|
|
|
|
# Starting at the beginning.
|
|
assert full_node_api.full_node.blockchain.get_peak_height() is None
|
|
|
|
await full_node_api.farm_blocks_to_puzzlehash(count=count)
|
|
|
|
# The requested number of blocks had been processed.
|
|
expected_height = None if count == 0 else count
|
|
assert full_node_api.full_node.blockchain.get_peak_height() == expected_height
|
|
|
|
@pytest.mark.anyio
|
|
@pytest.mark.parametrize(argnames="count", argvalues=[0, 1, 2, 5, 10])
|
|
async def test_simulation_farm_blocks(
|
|
self,
|
|
self_hostname: str,
|
|
count,
|
|
simulator_and_wallet: OldSimulatorsAndWallets,
|
|
):
|
|
[[full_node_api], [[wallet_node, wallet_server]], _] = simulator_and_wallet
|
|
|
|
await wallet_server.start_client(PeerInfo(self_hostname, full_node_api.server.get_port()), None)
|
|
|
|
# Avoiding an attribute error below.
|
|
assert wallet_node.wallet_state_manager is not None
|
|
|
|
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
|
|
# Starting at the beginning.
|
|
assert full_node_api.full_node.blockchain.get_peak_height() is None
|
|
|
|
rewards = await full_node_api.farm_blocks_to_wallet(count=count, wallet=wallet)
|
|
|
|
# The requested number of blocks had been processed plus 1 to handle the final reward
|
|
# transactions in the case of a non-zero count. An additional +1 is added for the genesis block which does not
|
|
# farm rewards to the wallet.
|
|
expected_height = count
|
|
if count > 0:
|
|
expected_height += 2
|
|
|
|
peak_height = full_node_api.full_node.blockchain.get_peak_height()
|
|
if peak_height is None:
|
|
peak_height = uint32(0)
|
|
|
|
assert peak_height == expected_height
|
|
|
|
# The expected rewards have been received and confirmed.
|
|
unconfirmed_balance = await wallet.get_unconfirmed_balance()
|
|
confirmed_balance = await wallet.get_confirmed_balance()
|
|
assert [unconfirmed_balance, confirmed_balance] == [rewards, rewards]
|
|
|
|
# Test that we can change the time per block
|
|
new_time_per_block = uint64(1000)
|
|
full_node_api.time_per_block = new_time_per_block
|
|
if count > 0:
|
|
peak = full_node_api.full_node.blockchain.get_peak()
|
|
assert isinstance(peak, BlockRecord)
|
|
start_time = peak.timestamp
|
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32.zeros))
|
|
peak = full_node_api.full_node.blockchain.get_peak()
|
|
assert isinstance(peak, BlockRecord)
|
|
end_time = peak.timestamp
|
|
assert isinstance(start_time, uint64)
|
|
assert isinstance(end_time, uint64)
|
|
assert end_time - start_time >= new_time_per_block
|
|
|
|
@pytest.mark.anyio
|
|
@pytest.mark.parametrize(
|
|
argnames=["amount", "coin_count"],
|
|
argvalues=[
|
|
[0, 0],
|
|
[1, 2],
|
|
[(2 * units["chia"]) - 1, 2],
|
|
[2 * units["chia"], 2],
|
|
[(2 * units["chia"]) + 1, 4],
|
|
[3 * units["chia"], 4],
|
|
[10 * units["chia"], 10],
|
|
],
|
|
)
|
|
async def test_simulation_farm_rewards(
|
|
self,
|
|
self_hostname: str,
|
|
amount: int,
|
|
coin_count: int,
|
|
simulator_and_wallet: OldSimulatorsAndWallets,
|
|
):
|
|
[[full_node_api], [[wallet_node, wallet_server]], _] = simulator_and_wallet
|
|
|
|
await wallet_server.start_client(PeerInfo(self_hostname, full_node_api.server.get_port()), None)
|
|
|
|
# Avoiding an attribute error below.
|
|
assert wallet_node.wallet_state_manager is not None
|
|
|
|
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
|
|
rewards = await full_node_api.farm_rewards_to_wallet(amount=amount, wallet=wallet)
|
|
|
|
# At least the requested amount was farmed.
|
|
assert rewards >= amount
|
|
|
|
# The rewards amount is both received and confirmed.
|
|
unconfirmed_balance = await wallet.get_unconfirmed_balance()
|
|
confirmed_balance = await wallet.get_confirmed_balance()
|
|
assert [unconfirmed_balance, confirmed_balance] == [rewards, rewards]
|
|
|
|
# The expected number of coins were received.
|
|
spendable_coins = await wallet.wallet_state_manager.get_spendable_coins_for_wallet(wallet.id())
|
|
assert len(spendable_coins) == coin_count
|
|
|
|
@pytest.mark.anyio
|
|
async def test_wait_transaction_records_entered_mempool(
|
|
self,
|
|
self_hostname: str,
|
|
simulator_and_wallet: OldSimulatorsAndWallets,
|
|
) -> None:
|
|
repeats = 50
|
|
tx_amount = uint64(1)
|
|
[[full_node_api], [[wallet_node, wallet_server]], _] = simulator_and_wallet
|
|
|
|
await wallet_server.start_client(PeerInfo(self_hostname, full_node_api.server.get_port()), None)
|
|
|
|
# Avoiding an attribute hint issue below.
|
|
assert wallet_node.wallet_state_manager is not None
|
|
|
|
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
|
|
# generate some coins for repetitive testing
|
|
await full_node_api.farm_rewards_to_wallet(amount=repeats * tx_amount, wallet=wallet)
|
|
coins = await full_node_api.create_coins_with_amounts(amounts=[tx_amount] * repeats, wallet=wallet)
|
|
assert len(coins) == repeats
|
|
|
|
# repeating just to try to expose any flakiness
|
|
for coin in coins:
|
|
async with wallet.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope:
|
|
await wallet.generate_signed_transaction(
|
|
amounts=[uint64(tx_amount)],
|
|
puzzle_hashes=[await action_scope.get_puzzle_hash(wallet.wallet_state_manager)],
|
|
action_scope=action_scope,
|
|
coins={coin},
|
|
)
|
|
|
|
[tx] = action_scope.side_effects.transactions
|
|
await full_node_api.wait_transaction_records_entered_mempool(records=action_scope.side_effects.transactions)
|
|
assert tx.spend_bundle is not None
|
|
assert full_node_api.full_node.mempool_manager.get_spendbundle(tx.spend_bundle.name()) is not None
|
|
# TODO: this fails but it seems like it shouldn't when above passes
|
|
# assert tx.is_in_mempool()
|
|
|
|
@pytest.mark.parametrize(argnames="records_or_bundles_or_coins", argvalues=["records", "bundles", "coins"])
|
|
@pytest.mark.anyio
|
|
async def test_process_transactions(
|
|
self,
|
|
self_hostname: str,
|
|
simulator_and_wallet: OldSimulatorsAndWallets,
|
|
records_or_bundles_or_coins: str,
|
|
) -> None:
|
|
repeats = 20
|
|
tx_amount = uint64(1)
|
|
tx_per_repeat = 2
|
|
[[full_node_api], [[wallet_node, wallet_server]], _] = simulator_and_wallet
|
|
|
|
await wallet_server.start_client(PeerInfo(self_hostname, full_node_api.server.get_port()), None)
|
|
|
|
# Avoiding an attribute hint issue below.
|
|
assert wallet_node.wallet_state_manager is not None
|
|
|
|
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
|
|
# generate some coins for repetitive testing
|
|
await full_node_api.farm_rewards_to_wallet(amount=tx_amount * repeats * tx_per_repeat, wallet=wallet)
|
|
all_coins = await full_node_api.create_coins_with_amounts(
|
|
amounts=[tx_amount] * repeats * tx_per_repeat, wallet=wallet
|
|
)
|
|
assert len(all_coins) == repeats * tx_per_repeat
|
|
|
|
coins_iter = iter(all_coins)
|
|
# repeating just to try to expose any flakiness
|
|
for repeat in range(repeats):
|
|
coins = [next(coins_iter) for _ in range(tx_per_repeat)]
|
|
async with wallet.wallet_state_manager.new_action_scope(
|
|
DEFAULT_TX_CONFIG, push=True, merge_spends=False
|
|
) as action_scope:
|
|
for coin in coins:
|
|
await wallet.generate_signed_transaction(
|
|
amounts=[uint64(tx_amount)],
|
|
puzzle_hashes=[await action_scope.get_puzzle_hash(wallet.wallet_state_manager)],
|
|
action_scope=action_scope,
|
|
coins={coin},
|
|
)
|
|
|
|
for tx in action_scope.side_effects.transactions:
|
|
assert tx.spend_bundle is not None, "the above created transaction is missing the expected spend bundle"
|
|
|
|
transactions = action_scope.side_effects.transactions
|
|
|
|
if records_or_bundles_or_coins == "records":
|
|
await full_node_api.process_transaction_records(records=transactions)
|
|
elif records_or_bundles_or_coins == "bundles":
|
|
await full_node_api.process_spend_bundles(
|
|
bundles=[tx.spend_bundle for tx in transactions if tx.spend_bundle is not None]
|
|
)
|
|
elif records_or_bundles_or_coins == "coins":
|
|
await full_node_api.process_coin_spends(
|
|
coins=[
|
|
coin
|
|
for tx in transactions
|
|
if tx.spend_bundle is not None
|
|
for coin in tx.spend_bundle.additions()
|
|
]
|
|
)
|
|
else:
|
|
raise Exception("unexpected parametrization")
|
|
for coin in coins:
|
|
coin_record = await full_node_api.full_node.coin_store.get_coin_record(coin.name())
|
|
assert coin_record is not None
|
|
|
|
@pytest.mark.anyio
|
|
@pytest.mark.parametrize(
|
|
argnames="amounts",
|
|
argvalues=[
|
|
[uint64(0)],
|
|
# cheating on type since -5 can't be heald in a proper uint64
|
|
[uint64(5), -5],
|
|
[uint64(4), uint64(0)],
|
|
],
|
|
ids=lambda amounts: ", ".join(str(amount) for amount in amounts),
|
|
)
|
|
async def test_create_coins_with_invalid_amounts_raises(
|
|
self,
|
|
self_hostname: str,
|
|
amounts: list[uint64],
|
|
simulator_and_wallet: OldSimulatorsAndWallets,
|
|
) -> None:
|
|
[[full_node_api], [[wallet_node, wallet_server]], _] = simulator_and_wallet
|
|
|
|
await wallet_server.start_client(PeerInfo(self_hostname, full_node_api.server.get_port()), None)
|
|
|
|
# Avoiding an attribute hint issue below.
|
|
assert wallet_node.wallet_state_manager is not None
|
|
|
|
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
|
|
with pytest.raises(Exception, match="Coins must have a positive value"):
|
|
await full_node_api.create_coins_with_amounts(amounts=amounts, wallet=wallet)
|
|
|
|
@pytest.mark.limit_consensus_modes(reason="This test only supports one running at a time.")
|
|
@pytest.mark.anyio
|
|
async def test_introducer_fallback_to_dns(self, simulation):
|
|
full_system: FullSystem
|
|
full_system, _ = simulation
|
|
introducer = full_system.introducer
|
|
introducer.introducer.dns_servers = ["127.0.0.1"]
|
|
introducer.introducer.resolver = FakeDNSResolver()
|
|
peers = await introducer.introducer.get_peers_from_dns(num_peers=10)
|
|
assert len(peers) == 2
|
|
assert any(peer.host == "1.2.3.4" for peer in peers)
|
|
assert any(peer.host == "::1" for peer in peers)
|