diff --git a/chia/cmds/wallet_funcs.py b/chia/cmds/wallet_funcs.py index 7588e4188f..1ff94f5bca 100644 --- a/chia/cmds/wallet_funcs.py +++ b/chia/cmds/wallet_funcs.py @@ -19,6 +19,7 @@ from chia.cmds.cmds_util import ( ) from chia.cmds.peer_funcs import print_connections from chia.cmds.units import units +from chia.rpc.wallet_request_types import GetNotifications from chia.rpc.wallet_rpc_client import WalletRpcClient from chia.types.blockchain_format.sized_bytes import bytes32 from chia.util.bech32m import bech32_decode, decode_puzzle_hash, encode_puzzle_hash @@ -1388,10 +1389,12 @@ async def get_notifications( if ids is not None and len(ids) == 0: ids = None - notifications = await wallet_client.get_notifications(ids=ids, pagination=(start, end)) - for notification in notifications: + response = await wallet_client.get_notifications( + GetNotifications(ids=ids, start=uint32.construct_optional(start), end=uint32.construct_optional(end)) + ) + for notification in response.notifications: print("") - print(f"ID: {notification.coin_id.hex()}") + print(f"ID: {notification.id.hex()}") print(f"message: {notification.message.decode('utf-8')}") print(f"amount: {notification.amount}") diff --git a/chia/consensus/block_body_validation.py b/chia/consensus/block_body_validation.py index 0dd2dcf5c4..f5f6cae1ca 100644 --- a/chia/consensus/block_body_validation.py +++ b/chia/consensus/block_body_validation.py @@ -118,6 +118,13 @@ class ForkInfo: assert coin.name() not in self.additions_since_fork self.additions_since_fork[coin.name()] = ForkAdd(coin, uint32(block.height), uint64(timestamp), None, True) + def rollback(self, header_hash: bytes32, height: int) -> None: + assert height <= self.peak_height + self.peak_height = height + self.peak_hash = header_hash + self.additions_since_fork = {k: v for k, v in self.additions_since_fork.items() if v.confirmed_height <= height} + self.removals_since_fork = {k: v for k, v in self.removals_since_fork.items() if v.height <= height} + async def validate_block_body( constants: ConsensusConstants, @@ -401,6 +408,7 @@ async def validate_block_body( # and coins created after fork (additions_since_fork) if rem in fork_info.removals_since_fork: # This coin was spent in the fork + log.error(f"Err.DOUBLE_SPEND_IN_FORK {fork_info.removals_since_fork[rem]}") return Err.DOUBLE_SPEND_IN_FORK, None removals_from_db.append(rem) @@ -433,7 +441,7 @@ async def validate_block_body( # This coin is not in the current heaviest chain, so it must be in the fork if rem not in fork_info.additions_since_fork: # Check for spending a coin that does not exist in this fork - log.error(f"Err.UNKNOWN_UNSPENT: COIN ID: {rem} NPC RESULT: {npc_result}") + log.error(f"Err.UNKNOWN_UNSPENT: COIN ID: {rem} fork_info: {fork_info}") return Err.UNKNOWN_UNSPENT, None addition: ForkAdd = fork_info.additions_since_fork[rem] new_coin_record: CoinRecord = CoinRecord( diff --git a/chia/consensus/blockchain.py b/chia/consensus/blockchain.py index d8678f8b88..f9228e0fd8 100644 --- a/chia/consensus/blockchain.py +++ b/chia/consensus/blockchain.py @@ -194,6 +194,26 @@ class Blockchain(BlockchainInterface): return None return self.height_to_block_record(self._peak_height) + def get_tx_peak(self) -> Optional[BlockRecord]: + """ + Return the most recent transaction block. i.e. closest to the peak of the blockchain + Requires the blockchain to be initialized and there to be a peak set + """ + + if self._peak_height is None: + return None + tx_height = self._peak_height + tx_peak = self.height_to_block_record(tx_height) + while not tx_peak.is_transaction_block: + # it seems BlockTools only produce chains where the first block is a + # transaction block, which makes it hard to test this case + if tx_height == 0: # pragma: no cover + return None + tx_height = uint32(tx_height - 1) + tx_peak = self.height_to_block_record(tx_height) + + return tx_peak + async def get_full_peak(self) -> Optional[FullBlock]: if self._peak_height is None: return None @@ -465,6 +485,13 @@ class Blockchain(BlockchainInterface): self._peak_height = block_record.height except BaseException as e: + # depending on exactly when the failure of adding the block + # happened, we may not have added it to the block record cache + try: + self.remove_block_record(header_hash) + except KeyError: + pass + fork_info.rollback(header_hash, -1 if previous_peak_height is None else previous_peak_height) self.block_store.rollback_cache_block(header_hash) self._peak_height = previous_peak_height log.error( diff --git a/chia/full_node/coin_store.py b/chia/full_node/coin_store.py index dcfbef07a9..b741687a67 100644 --- a/chia/full_node/coin_store.py +++ b/chia/full_node/coin_store.py @@ -411,6 +411,97 @@ class CoinStore: return coins + async def batch_coin_states_by_puzzle_hashes( + self, + puzzle_hashes: List[bytes32], + *, + min_height: uint32 = uint32(0), + include_spent: bool = True, + include_unspent: bool = True, + include_hinted: bool = True, + max_items: int = 50000, + ) -> Tuple[List[CoinState], Optional[uint32]]: + """ + Returns the coin states, as well as the next block height (or `None` if finished). + Note that the maximum number of puzzle hashes is currently set to 15000. + """ + + # This number is chosen such that it's below half of the Python 3.8+ SQLite variable limit. + # It can be changed later without breaking the protocol, but this is a practical limit for now. + assert len(puzzle_hashes) <= 15000 + + coin_states: List[CoinState] = [] + + async with self.db_wrapper.reader_no_transaction() as conn: + puzzle_hashes_db = tuple(puzzle_hashes) + puzzle_hash_count = len(puzzle_hashes_db) + + if include_hinted: + require_spent = "cr.spent_index>0" + require_unspent = "cr.spent_index=0" + else: + require_spent = "spent_index>0" + require_unspent = "spent_index=0" + + if include_spent and include_unspent: + height_filter = "" + elif include_spent: + height_filter = f"AND {require_spent}" + elif include_unspent: + height_filter = f"AND {require_unspent}" + else: + # There are no coins which are both spent and unspent, so we're finished. + return [], None + + if include_hinted: + cursor = await conn.execute( + f"SELECT cr.confirmed_index, cr.spent_index, cr.coinbase, cr.puzzle_hash, " + f"cr.coin_parent, cr.amount, cr.timestamp FROM coin_record cr " + f"LEFT JOIN hints h ON cr.coin_name = h.coin_id " + f'WHERE (cr.puzzle_hash in ({"?," * (puzzle_hash_count - 1)}?) ' + f'OR h.hint in ({"?," * (puzzle_hash_count - 1)}?)) ' + f"AND (cr.confirmed_index>=? OR cr.spent_index>=?) " + f"{height_filter} " + f"ORDER BY MAX(cr.confirmed_index, cr.spent_index) ASC " + f"LIMIT ?", + puzzle_hashes_db + puzzle_hashes_db + (min_height, min_height, max_items + 1), + ) + else: + cursor = await conn.execute( + f"SELECT confirmed_index, spent_index, coinbase, puzzle_hash, " + f"coin_parent, amount, timestamp FROM coin_record INDEXED BY coin_puzzle_hash " + f'WHERE puzzle_hash in ({"?," * (puzzle_hash_count - 1)}?) ' + f"AND (confirmed_index>=? OR spent_index>=?) " + f"{height_filter} " + f"ORDER BY MAX(confirmed_index, spent_index) ASC " + f"LIMIT ?", + puzzle_hashes_db + (min_height, min_height, max_items + 1), + ) + + for row in await cursor.fetchall(): + coin_states.append(self.row_to_coin_state(row)) + + # If there aren't too many coin states, we've finished syncing these hashes. + # There is no next height to start from, so return `None`. + if len(coin_states) <= max_items: + return coin_states, None + + # The last item is the start of the next batch of coin states. + next_coin_state = coin_states.pop() + next_height = uint32(max(next_coin_state.created_height or 0, next_coin_state.spent_height or 0)) + + # In order to prevent blocks from being split up between batches, remove + # all coin states whose max height is the same as the last coin state's height. + while len(coin_states) > 0: + last_coin_state = coin_states[-1] + height = uint32(max(last_coin_state.created_height or 0, last_coin_state.spent_height or 0)) + if height != next_height: + break + + coin_states.pop() + + return coin_states, next_height + async def rollback_to_block(self, block_index: int) -> List[CoinRecord]: """ Note that block_index can be negative, in which case everything is rolled back diff --git a/chia/full_node/full_node.py b/chia/full_node/full_node.py index 2e2ff0703f..90581c2214 100644 --- a/chia/full_node/full_node.py +++ b/chia/full_node/full_node.py @@ -316,7 +316,7 @@ class FullNode: f"time taken: {int(time_taken)}s" ) async with self.blockchain.priority_mutex.acquire(priority=BlockchainMutexPriority.high): - pending_tx = await self.mempool_manager.new_peak(peak, None) + pending_tx = await self.mempool_manager.new_peak(self.blockchain.get_tx_peak(), None) assert len(pending_tx) == 0 # no pending transactions when starting up full_peak: Optional[FullBlock] = await self.blockchain.get_full_peak() @@ -1544,9 +1544,8 @@ class FullNode: # Update the mempool (returns successful pending transactions added to the mempool) spent_coins: List[bytes32] = [coin_id for coin_id, _ in state_change_summary.removals] - mempool_new_peak_result: List[Tuple[SpendBundle, NPCResult, bytes32]] = await self.mempool_manager.new_peak( - self.blockchain.get_peak(), spent_coins - ) + mempool_new_peak_result: List[Tuple[SpendBundle, NPCResult, bytes32]] + mempool_new_peak_result = await self.mempool_manager.new_peak(self.blockchain.get_tx_peak(), spent_coins) # Check if we detected a spent transaction, to load up our generator cache if block.transactions_generator is not None and self.full_node_store.previous_generator is None: diff --git a/chia/full_node/mempool_manager.py b/chia/full_node/mempool_manager.py index 34f4ff4e20..93b08c5b00 100644 --- a/chia/full_node/mempool_manager.py +++ b/chia/full_node/mempool_manager.py @@ -620,6 +620,11 @@ class MempoolManager: ) -> List[Tuple[SpendBundle, NPCResult, bytes32]]: """ Called when a new peak is available, we try to recreate a mempool for the new tip. + new_peak should always be the most recent *transaction* block of the chain. Since + the mempool cannot traverse the chain to find the most recent transaction block, + we wouldn't be able to detect, and correctly update the mempool, if we saw a + non-transaction block on a fork. self.peak must always be set to a transaction + block. """ if new_peak is None: return [] diff --git a/chia/rpc/wallet_request_types.py b/chia/rpc/wallet_request_types.py new file mode 100644 index 0000000000..bb84d5e9a1 --- /dev/null +++ b/chia/rpc/wallet_request_types.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import List, Optional + +from chia.types.blockchain_format.sized_bytes import bytes32 +from chia.util.ints import uint32 +from chia.util.streamable import Streamable, streamable +from chia.wallet.notification_store import Notification + + +@streamable +@dataclass(frozen=True) +class GetNotifications(Streamable): + ids: Optional[List[bytes32]] = None + start: Optional[uint32] = None + end: Optional[uint32] = None + + +@streamable +@dataclass(frozen=True) +class GetNotificationsResponse(Streamable): + notifications: List[Notification] diff --git a/chia/rpc/wallet_rpc_api.py b/chia/rpc/wallet_rpc_api.py index 603609951e..2805f6c308 100644 --- a/chia/rpc/wallet_rpc_api.py +++ b/chia/rpc/wallet_rpc_api.py @@ -18,7 +18,8 @@ from chia.pools.pool_wallet_info import FARMING_TO_POOL, PoolState, PoolWalletIn from chia.protocols.protocol_message_types import ProtocolMessageTypes from chia.protocols.wallet_protocol import CoinState from chia.rpc.rpc_server import Endpoint, EndpointResult, default_get_connections -from chia.rpc.util import tx_endpoint +from chia.rpc.util import marshal, tx_endpoint +from chia.rpc.wallet_request_types import GetNotifications, GetNotificationsResponse from chia.server.outbound_message import NodeType, make_msg from chia.server.ws_connection import WSChiaConnection from chia.simulator.simulator_protocol import FarmNewBlockProtocol @@ -1413,34 +1414,22 @@ class WalletRpcApi: return {"success": True, "index": updated_index} - async def get_notifications(self, request: Dict[str, Any]) -> EndpointResult: - ids: Optional[List[str]] = request.get("ids", None) - start: Optional[int] = request.get("start", None) - end: Optional[int] = request.get("end", None) - if ids is None: + @marshal + async def get_notifications(self, request: GetNotifications) -> GetNotificationsResponse: + if request.ids is None: notifications: List[ Notification ] = await self.service.wallet_state_manager.notification_manager.notification_store.get_all_notifications( - pagination=(start, end) + pagination=(request.start, request.end) ) else: notifications = ( await self.service.wallet_state_manager.notification_manager.notification_store.get_notifications( - [bytes32.from_hexstr(id) for id in ids] + request.ids ) ) - return { - "notifications": [ - { - "id": notification.coin_id.hex(), - "message": notification.message.hex(), - "amount": notification.amount, - "height": notification.height, - } - for notification in notifications - ] - } + return GetNotificationsResponse(notifications) async def delete_notifications(self, request: Dict[str, Any]) -> EndpointResult: ids: Optional[List[str]] = request.get("ids", None) @@ -3058,11 +3047,7 @@ class WalletRpcApi: else: nfts = await self.service.wallet_state_manager.nft_store.get_nft_list(start_index=start_index, count=count) for nft in nfts: - nft_info = await nft_puzzles.get_nft_info_from_puzzle( - nft, - self.service.wallet_state_manager.config, - request.get("ignore_size_limit", False), - ) + nft_info = await nft_puzzles.get_nft_info_from_puzzle(nft, self.service.wallet_state_manager.config) nft_info_list.append(nft_info) return {"wallet_id": wallet_id, "success": True, "nft_list": nft_info_list} @@ -3442,7 +3427,6 @@ class WalletRpcApi: uint32(coin_state.created_height) if coin_state.created_height else uint32(0), ), self.service.wallet_state_manager.config, - request.get("ignore_size_limit", False), ) # This is a bit hacky, it should just come out like this, but this works for this RPC nft_info = dataclasses.replace(nft_info, p2_address=p2_puzzle_hash) diff --git a/chia/rpc/wallet_rpc_client.py b/chia/rpc/wallet_rpc_client.py index f8c315e643..c7093888cc 100644 --- a/chia/rpc/wallet_rpc_client.py +++ b/chia/rpc/wallet_rpc_client.py @@ -5,6 +5,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union, cast from chia.data_layer.data_layer_wallet import Mirror, SingletonRecord from chia.pools.pool_wallet_info import PoolWalletInfo from chia.rpc.rpc_client import RpcClient +from chia.rpc.wallet_request_types import GetNotifications, GetNotificationsResponse 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 @@ -13,7 +14,6 @@ from chia.types.spend_bundle import SpendBundle from chia.util.bech32m import encode_puzzle_hash from chia.util.ints import uint16, uint32, uint64 from chia.wallet.conditions import Condition, ConditionValidTimes, conditions_to_json_dicts -from chia.wallet.notification_store import Notification from chia.wallet.trade_record import TradeRecord from chia.wallet.trading.offer import Offer from chia.wallet.transaction_record import TransactionRecord @@ -1197,27 +1197,8 @@ class WalletRpcClient(RpcClient): ) return [TransactionRecord.from_json_dict_convenience(tx) for tx in response["transactions"]] - async def get_notifications( - self, ids: Optional[List[bytes32]] = None, pagination: Optional[Tuple[Optional[int], Optional[int]]] = None - ) -> List[Notification]: - request: Dict[str, Any] = {} - if ids is not None: - request["ids"] = [id.hex() for id in ids] - if pagination is not None: - if pagination[0] is not None: - request["start"] = pagination[0] - if pagination[1] is not None: - request["end"] = pagination[1] - response = await self.fetch("get_notifications", request) - return [ - Notification( - bytes32.from_hexstr(notification["id"]), - bytes.fromhex(notification["message"]), - uint64(notification["amount"]), - uint32(notification["height"]), - ) - for notification in response["notifications"] - ] + async def get_notifications(self, request: GetNotifications) -> GetNotificationsResponse: + return GetNotificationsResponse.from_json_dict(await self.fetch("get_notifications", request.to_json_dict())) async def delete_notifications(self, ids: Optional[List[bytes32]] = None) -> bool: request = {} diff --git a/chia/server/start_full_node.py b/chia/server/start_full_node.py index 2fd94ecb49..63cfa95c2b 100644 --- a/chia/server/start_full_node.py +++ b/chia/server/start_full_node.py @@ -16,7 +16,7 @@ from chia.server.outbound_message import NodeType from chia.server.start_service import RpcInfo, Service, async_run from chia.types.aliases import FullNodeService from chia.util.chia_logging import initialize_service_logging -from chia.util.config import load_config, load_config_cli +from chia.util.config import get_unresolved_peer_infos, load_config, load_config_cli from chia.util.default_root import DEFAULT_ROOT_PATH from chia.util.ints import uint16 from chia.util.misc import SignalHandlers @@ -61,6 +61,7 @@ async def create_full_node_service( advertised_port=service_config["port"], service_name=SERVICE_NAME, upnp_ports=upnp_list, + connect_peers=get_unresolved_peer_infos(service_config, NodeType.FULL_NODE), on_connect_callback=full_node.on_connect, network_id=network_id, rpc_info=rpc_info, diff --git a/chia/util/initial-config.yaml b/chia/util/initial-config.yaml index fa35189183..02b8f05f6c 100644 --- a/chia/util/initial-config.yaml +++ b/chia/util/initial-config.yaml @@ -353,6 +353,8 @@ timelord: full_node: # The full node server (if run) will run on this port port: 8444 + # The full node will attempt to connect to these full nodes + full_node_peers: [] # controls the sync-to-disk behavior of the database connection. Can be one of: # "on" enables syncing to disk, minimizes risk of corrupting the DB in diff --git a/chia/wallet/nft_wallet/nft_puzzles.py b/chia/wallet/nft_wallet/nft_puzzles.py index 85a9fad8e1..111114d4bc 100644 --- a/chia/wallet/nft_wallet/nft_puzzles.py +++ b/chia/wallet/nft_wallet/nft_puzzles.py @@ -1,7 +1,7 @@ from __future__ import annotations import logging -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple, cast from clvm.casts import int_from_bytes from clvm_tools.binutils import disassemble @@ -58,11 +58,7 @@ def create_nft_layer_puzzle_with_curry_params( def create_full_puzzle_with_nft_puzzle(singleton_id: bytes32, inner_puzzle: Program) -> Program: if log.isEnabledFor(logging.DEBUG): - log.debug( - "Creating full NFT puzzle with inner puzzle: \n%r\n%r", - singleton_id, - inner_puzzle.get_tree_hash(), - ) + log.debug("Creating full NFT puzzle with inner puzzle: \n%r\n%r", singleton_id, inner_puzzle.get_tree_hash()) singleton_struct = Program.to((SINGLETON_MOD_HASH, (singleton_id, LAUNCHER_PUZZLE_HASH))) full_puzzle = SINGLETON_TOP_LAYER_MOD.curry(singleton_struct, inner_puzzle) @@ -91,9 +87,7 @@ def create_full_puzzle( return full_puzzle -async def get_nft_info_from_puzzle( - nft_coin_info: NFTCoinInfo, config: Dict[str, Any], ignore_size_limit: bool = False -) -> NFTInfo: +async def get_nft_info_from_puzzle(nft_coin_info: NFTCoinInfo, config: Dict[str, Any]) -> NFTInfo: """ Extract NFT info from a full puzzle :param nft_coin_info NFTCoinInfo in local database @@ -175,7 +169,6 @@ def prepend_value(key: bytes, value: Program, metadata: Dict[bytes, Any]) -> Non :param metadata: Metadata :return: """ - if value != Program.to(0): if metadata[key] == b"": metadata[key] = [value.as_python()] @@ -201,12 +194,7 @@ def construct_ownership_layer( transfer_program: Program, inner_puzzle: Program, ) -> Program: - return NFT_OWNERSHIP_LAYER.curry( - NFT_OWNERSHIP_LAYER_HASH, - current_owner, - transfer_program, - inner_puzzle, - ) + return NFT_OWNERSHIP_LAYER.curry(NFT_OWNERSHIP_LAYER_HASH, current_owner, transfer_program, inner_puzzle) def create_ownership_layer_puzzle( @@ -226,11 +214,7 @@ def create_ownership_layer_puzzle( singleton_struct = Program.to((SINGLETON_MOD_HASH, (nft_id, LAUNCHER_PUZZLE_HASH))) if not royalty_puzzle_hash: royalty_puzzle_hash = p2_puzzle.get_tree_hash() - transfer_program = NFT_TRANSFER_PROGRAM_DEFAULT.curry( - singleton_struct, - royalty_puzzle_hash, - percentage, - ) + transfer_program = NFT_TRANSFER_PROGRAM_DEFAULT.curry(singleton_struct, royalty_puzzle_hash, percentage) nft_inner_puzzle = p2_puzzle nft_ownership_layer_puzzle = construct_ownership_layer( @@ -240,10 +224,7 @@ def create_ownership_layer_puzzle( def create_ownership_layer_transfer_solution( - new_did: bytes, - new_did_inner_hash: bytes, - trade_prices_list: List[List[int]], - new_puzhash: bytes32, + new_did: bytes, new_did_inner_hash: bytes, trade_prices_list: List[List[int]], new_puzhash: bytes32 ) -> Program: log.debug( "Creating a transfer solution with: DID:%s Inner_puzhash:%s trade_price:%s puzhash:%s", @@ -252,23 +233,12 @@ def create_ownership_layer_transfer_solution( str(trade_prices_list), new_puzhash.hex(), ) - condition_list = [ - [ - 51, - new_puzhash, - 1, - [new_puzhash], - ], - [-10, new_did, trade_prices_list, new_did_inner_hash], - ] + condition_list = [[51, new_puzhash, 1, [new_puzhash]], [-10, new_did, trade_prices_list, new_did_inner_hash]] log.debug("Condition list raw: %r", condition_list) - solution = Program.to( - [ - [solution_for_conditions(condition_list)], - ] - ) + solution = Program.to([[solution_for_conditions(condition_list)]]) log.debug("Generated transfer solution: %s", solution) - return solution + # TODO: Remove cast when we improve typing + return cast(Program, solution) def get_metadata_and_phs(unft: UncurriedNFT, solution: SerializedProgram) -> Tuple[Program, bytes32]: diff --git a/chia/wallet/notification_store.py b/chia/wallet/notification_store.py index a3c1421f34..bce01742bc 100644 --- a/chia/wallet/notification_store.py +++ b/chia/wallet/notification_store.py @@ -8,11 +8,13 @@ from typing import List, Optional, Tuple from chia.types.blockchain_format.sized_bytes import bytes32 from chia.util.db_wrapper import DBWrapper2 from chia.util.ints import uint32, uint64 +from chia.util.streamable import Streamable, streamable +@streamable @dataclasses.dataclass(frozen=True) -class Notification: - coin_id: bytes32 +class Notification(Streamable): + id: bytes32 message: bytes amount: uint64 height: uint32 @@ -70,7 +72,7 @@ class NotificationStore: cursor = await conn.execute( "INSERT OR REPLACE INTO notifications (coin_id, msg, amount, height) VALUES(?, ?, ?, ?)", ( - notification.coin_id, + notification.id, notification.message, notification.amount.stream_to_bytes(), notification.height, @@ -78,7 +80,7 @@ class NotificationStore: ) cursor = await conn.execute( "INSERT OR REPLACE INTO all_notification_ids (coin_id) VALUES(?)", - (notification.coin_id,), + (notification.id,), ) await cursor.close() @@ -116,18 +118,25 @@ class NotificationStore: """ if pagination is not None: if pagination[1] is not None and pagination[0] is not None: - pagination_str = f" LIMIT {pagination[0]}, {pagination[1] - pagination[0]}" + pagination_str = " LIMIT ?, ?" + pagination_params: Tuple[int, ...] = (pagination[0], pagination[1] - pagination[0]) elif pagination[1] is None and pagination[0] is not None: - pagination_str = f" LIMIT {pagination[0]}, (SELECT COUNT(*) from notifications)" + pagination_str = " LIMIT ?, (SELECT COUNT(*) from notifications)" + pagination_params = (pagination[0],) elif pagination[1] is not None and pagination[0] is None: - pagination_str = f" LIMIT {pagination[1]}" + pagination_str = " LIMIT ?" + pagination_params = (pagination[1],) else: pagination_str = "" + pagination_params = tuple() else: pagination_str = "" + pagination_params = tuple() async with self.db_wrapper.reader_no_transaction() as conn: - rows = await conn.execute_fetchall(f"SELECT * from notifications ORDER BY amount DESC{pagination_str}") + rows = await conn.execute_fetchall( + f"SELECT * from notifications ORDER BY amount DESC{pagination_str}", pagination_params + ) return [ Notification( diff --git a/chia/wallet/puzzles/singleton_top_layer.py b/chia/wallet/puzzles/singleton_top_layer.py index 2f21b56713..9deab6717a 100644 --- a/chia/wallet/puzzles/singleton_top_layer.py +++ b/chia/wallet/puzzles/singleton_top_layer.py @@ -343,15 +343,10 @@ def claim_p2_singleton( # Get the CoinSpend for spending to a delayed puzzle def spend_to_delayed_puzzle( - p2_singleton_coin: Coin, - output_amount: uint64, - launcher_id: bytes32, - delay_time: uint64, - delay_ph: bytes32, + p2_singleton_coin: Coin, output_amount: uint64, launcher_id: bytes32, delay_time: uint64, delay_ph: bytes32 ) -> CoinSpend: - claim_coinsol = make_spend( + return make_spend( p2_singleton_coin, pay_to_singleton_or_delay_puzzle(launcher_id, delay_time, delay_ph), solution_for_p2_delayed_puzzle(output_amount), ) - return claim_coinsol diff --git a/mypy-exclusions.txt b/mypy-exclusions.txt index 9d91381b4b..c79f3a9cb9 100644 --- a/mypy-exclusions.txt +++ b/mypy-exclusions.txt @@ -22,7 +22,6 @@ chia.wallet.chialisp chia.wallet.did_wallet.did_wallet chia.wallet.key_val_store chia.wallet.lineage_proof -chia.wallet.nft_wallet.nft_puzzles chia.wallet.payment chia.wallet.puzzles.load_clvm chia.wallet.puzzles.p2_conditions @@ -105,7 +104,6 @@ tests.util.time_out_assert tests.wallet.cat_wallet.test_trades tests.wallet.did_wallet.test_did tests.wallet.rpc.test_wallet_rpc -tests.wallet.simple_sync.test_simple_sync_protocol tests.wallet.sync.test_wallet_sync tests.wallet.test_chialisp tests.wallet.test_singleton_lifecycle diff --git a/tests/blockchain/test_blockchain.py b/tests/blockchain/test_blockchain.py index aa3b001ced..6b6f9b9d8b 100644 --- a/tests/blockchain/test_blockchain.py +++ b/tests/blockchain/test_blockchain.py @@ -15,10 +15,12 @@ from clvm.casts import int_to_bytes from chia.consensus.block_body_validation import ForkInfo from chia.consensus.block_header_validation import validate_finished_header_block +from chia.consensus.block_record import BlockRecord from chia.consensus.block_rewards import calculate_base_farmer_reward from chia.consensus.blockchain import AddBlockResult, Blockchain from chia.consensus.coinbase import create_farmer_coin from chia.consensus.constants import ConsensusConstants +from chia.consensus.full_block_to_block_record import block_to_block_record from chia.consensus.multiprocess_validation import PreValidationResult from chia.consensus.pot_iterations import is_overflow_block from chia.full_node.bundle_tools import detect_potential_template_generator @@ -3101,6 +3103,12 @@ class TestBodyValidation: assert preval_results[0].error == Err.BAD_AGGREGATE_SIGNATURE.value +def maybe_header_hash(block: Optional[BlockRecord]) -> Optional[bytes32]: + if block is None: + return None + return block.header_hash + + class TestReorgs: @pytest.mark.anyio async def test_basic_reorg(self, empty_blockchain, bt): @@ -3121,6 +3129,46 @@ class TestReorgs: await _validate_and_add_block(b, reorg_block) assert b.get_peak().height == 16 + @pytest.mark.anyio + async def test_get_tx_peak_reorg(self, empty_blockchain, bt, consensus_mode: ConsensusMode): + b = empty_blockchain + + if consensus_mode == ConsensusMode.PLAIN: + reorg_point = 13 + else: + reorg_point = 12 + blocks = bt.get_consecutive_blocks(reorg_point) + + last_tx_block: Optional[bytes32] = None + for block in blocks: + assert maybe_header_hash(b.get_tx_peak()) == last_tx_block + await _validate_and_add_block(b, block) + if block.is_transaction_block(): + last_tx_block = block.header_hash + assert b.get_peak().height == reorg_point - 1 + assert maybe_header_hash(b.get_tx_peak()) == last_tx_block + + reorg_last_tx_block: Optional[bytes32] = None + + blocks_reorg_chain = bt.get_consecutive_blocks(7, blocks[:10], seed=b"2") + assert blocks_reorg_chain[reorg_point].is_transaction_block() is False + for reorg_block in blocks_reorg_chain: + if reorg_block.height < 10: + await _validate_and_add_block(b, reorg_block, expected_result=AddBlockResult.ALREADY_HAVE_BLOCK) + elif reorg_block.height < reorg_point: + await _validate_and_add_block(b, reorg_block, expected_result=AddBlockResult.ADDED_AS_ORPHAN) + elif reorg_block.height >= reorg_point: + await _validate_and_add_block(b, reorg_block) + + if reorg_block.is_transaction_block(): + reorg_last_tx_block = reorg_block.header_hash + if reorg_block.height >= reorg_point: + last_tx_block = reorg_last_tx_block + + assert maybe_header_hash(b.get_tx_peak()) == last_tx_block + + assert b.get_peak().height == 16 + @pytest.mark.anyio @pytest.mark.parametrize("light_blocks", [True, False]) async def test_long_reorg( @@ -3693,3 +3741,28 @@ async def test_reorg_flip_flop(empty_blockchain, bt): for block in chain_b[40:]: await _validate_and_add_block(b, block) + + +async def test_get_tx_peak(default_400_blocks, empty_blockchain): + bc = empty_blockchain + test_blocks = default_400_blocks[:100] + + res = await bc.pre_validate_blocks_multiprocessing(test_blocks, {}, validate_signatures=False) + + last_tx_block: Optional[FullBlock] = None + for b, prevalidation_res in zip(test_blocks, res): + assert bc.get_tx_peak() == last_tx_block + res, err, state = await bc.add_block(b, prevalidation_res) + assert err is None + + if b.is_transaction_block(): + block_record = block_to_block_record( + bc.constants, + bc, + prevalidation_res.required_iters, + b, + None, + ) + last_tx_block = block_record + + assert bc.get_tx_peak() == last_tx_block diff --git a/tests/cmds/wallet/test_notifications.py b/tests/cmds/wallet/test_notifications.py index b39fcbbbeb..dd85047ee7 100644 --- a/tests/cmds/wallet/test_notifications.py +++ b/tests/cmds/wallet/test_notifications.py @@ -3,6 +3,7 @@ from __future__ import annotations from pathlib import Path from typing import List, Optional, Tuple, cast +from chia.rpc.wallet_request_types import GetNotifications, GetNotificationsResponse from chia.types.blockchain_format.sized_bytes import bytes32 from chia.util.bech32m import encode_puzzle_hash from chia.util.ints import uint32, uint64 @@ -62,11 +63,11 @@ def test_notifications_get(capsys: object, get_test_cli_clients: Tuple[TestRpcCl # set RPC Client class NotificationsGetRpcClient(TestWalletRpcClient): - async def get_notifications( - self, ids: Optional[List[bytes32]] = None, pagination: Optional[Tuple[Optional[int], Optional[int]]] = None - ) -> List[Notification]: - self.add_to_log("get_notifications", (ids, pagination)) - return [Notification(get_bytes32(1), bytes("hello", "utf8"), uint64(1000000000), uint32(50))] + async def get_notifications(self, request: GetNotifications) -> GetNotificationsResponse: + self.add_to_log("get_notifications", (request,)) + return GetNotificationsResponse( + [Notification(get_bytes32(1), bytes("hello", "utf8"), uint64(1000000000), uint32(50))] + ) inst_rpc_client = NotificationsGetRpcClient() # pylint: disable=no-value-for-parameter test_rpc_clients.wallet_rpc_client = inst_rpc_client @@ -87,7 +88,7 @@ def test_notifications_get(capsys: object, get_test_cli_clients: Tuple[TestRpcCl "amount: 1000000000", ] run_cli_command_and_assert(capsys, root_dir, command_args, assert_list) - expected_calls: logType = {"get_notifications": [([get_bytes32(1)], (10, 10))]} + expected_calls: logType = {"get_notifications": [(GetNotifications([get_bytes32(1)], uint32(10), uint32(10)),)]} test_rpc_clients.wallet_rpc_client.check_log(expected_calls) diff --git a/tests/core/full_node/stores/test_coin_store.py b/tests/core/full_node/stores/test_coin_store.py index 818dde3a52..12dd038bfc 100644 --- a/tests/core/full_node/stores/test_coin_store.py +++ b/tests/core/full_node/stores/test_coin_store.py @@ -1,6 +1,7 @@ from __future__ import annotations import logging +from dataclasses import dataclass from pathlib import Path from typing import List, Optional, Set, Tuple @@ -11,7 +12,9 @@ from chia.consensus.blockchain import AddBlockResult, Blockchain from chia.consensus.coinbase import create_farmer_coin, create_pool_coin from chia.full_node.block_store import BlockStore from chia.full_node.coin_store import CoinStore +from chia.full_node.hint_store import HintStore from chia.full_node.mempool_check_conditions import get_name_puzzle_conditions +from chia.protocols.wallet_protocol import CoinState from chia.simulator.block_tools import BlockTools, test_constants from chia.simulator.wallet_tools import WalletTool from chia.types.blockchain_format.coin import Coin @@ -493,6 +496,192 @@ async def test_get_coin_states(db_version: int) -> None: assert len(await coin_store.get_coin_states_by_ids(True, coins, uint32(0), max_items=10000)) == 600 +@dataclass(frozen=True) +class RandomCoinRecords: + items: List[CoinRecord] + puzzle_hashes: List[bytes32] + hints: List[Tuple[bytes32, bytes]] + + +@pytest.fixture(scope="session") +def random_coin_records() -> RandomCoinRecords: + coin_records: List[CoinRecord] = [] + puzzle_hashes: List[bytes32] = [] + hints: List[Tuple[bytes32, bytes]] = [] + + for i in range(50000): + is_spent = i % 2 == 0 + is_hinted = i % 7 == 0 + created_height = uint32(i) + spent_height = uint32(created_height + 100) + + puzzle_hash = std_hash(i.to_bytes(4, byteorder="big")) + + coin = Coin( + std_hash(b"Parent Coin Id " + i.to_bytes(4, byteorder="big")), + puzzle_hash, + uint64(1000), + ) + + if is_hinted: + hint = std_hash(b"Hinted " + puzzle_hash) + hints.append((coin.name(), hint)) + puzzle_hashes.append(hint) + else: + puzzle_hashes.append(puzzle_hash) + + coin_records.append( + CoinRecord( + coin=coin, + confirmed_block_index=created_height, + spent_block_index=spent_height if is_spent else uint32(0), + coinbase=False, + timestamp=uint64(0), + ) + ) + + coin_records.sort(key=lambda cr: max(cr.confirmed_block_index, cr.spent_block_index)) + + return RandomCoinRecords(coin_records, puzzle_hashes, hints) + + +@pytest.mark.anyio +@pytest.mark.parametrize("include_spent", [True, False]) +@pytest.mark.parametrize("include_unspent", [True, False]) +@pytest.mark.parametrize("include_hinted", [True, False]) +async def test_coin_state_batches( + db_version: int, + random_coin_records: RandomCoinRecords, + include_spent: bool, + include_unspent: bool, + include_hinted: bool, +) -> None: + async with DBConnection(db_version) as db_wrapper: + # Initialize coin and hint stores. + coin_store = await CoinStore.create(db_wrapper) + hint_store = await HintStore.create(db_wrapper) + + await coin_store._add_coin_records(random_coin_records.items) + await hint_store.add_hints(random_coin_records.hints) + + # Make sure all of the coin states are found when batching. + ph_set = set(random_coin_records.puzzle_hashes) + expected_crs = [] + for cr in random_coin_records.items: + if cr.spent_block_index == 0 and not include_unspent: + continue + if cr.spent_block_index > 0 and not include_spent: + continue + if cr.coin.puzzle_hash not in ph_set and not include_hinted: + continue + expected_crs.append(cr) + + height: Optional[uint32] = uint32(0) + all_coin_states: List[CoinState] = [] + remaining_phs = random_coin_records.puzzle_hashes.copy() + + def height_of(coin_state: CoinState) -> int: + return max(coin_state.created_height or 0, coin_state.spent_height or 0) + + while height is not None: + (coin_states, height) = await coin_store.batch_coin_states_by_puzzle_hashes( + remaining_phs[:15000], + min_height=height, + include_spent=include_spent, + include_unspent=include_unspent, + include_hinted=include_hinted, + ) + + # Ensure that all of the returned coin states are in order. + assert all(height_of(coin_states[i]) <= height_of(coin_states[i + 1]) for i in range(len(coin_states) - 1)) + + all_coin_states += coin_states + + if height is None: + remaining_phs = remaining_phs[15000:] + + if len(remaining_phs) > 0: + height = uint32(0) + + assert len(all_coin_states) == len(expected_crs) + + all_coin_states.sort(key=height_of) + + for i in range(len(expected_crs)): + actual = all_coin_states[i] + expected = expected_crs[i] + + assert actual.coin == expected.coin, i + assert uint32(actual.created_height or 0) == expected.confirmed_block_index, i + assert uint32(actual.spent_height or 0) == expected.spent_block_index, i + + +@pytest.mark.anyio +@pytest.mark.parametrize("cut_off_middle", [True, False]) +async def test_batch_many_coin_states(db_version: int, cut_off_middle: bool) -> None: + async with DBConnection(db_version) as db_wrapper: + ph = bytes32(b"0" * 32) + + # Generate coin records. + coin_records: List[CoinRecord] = [] + count = 50000 + + for i in range(count): + # Create coin records at either height 10 or 12. + created_height = uint32((i % 2) * 2 + 10) + coin = Coin( + std_hash(b"Parent Coin Id " + i.to_bytes(4, byteorder="big")), + ph, + uint64(i), + ) + coin_records.append( + CoinRecord( + coin=coin, + confirmed_block_index=created_height, + spent_block_index=uint32(0), + coinbase=False, + timestamp=uint64(0), + ) + ) + + # Initialize coin and hint stores. + coin_store = await CoinStore.create(db_wrapper) + await HintStore.create(db_wrapper) + + await coin_store._add_coin_records(coin_records) + + # Make sure all of the coin states are found. + (all_coin_states, next_height) = await coin_store.batch_coin_states_by_puzzle_hashes([ph]) + all_coin_states.sort(key=lambda cs: cs.coin.amount) + + assert next_height is None + assert len(all_coin_states) == len(coin_records) + + for i in range(min(len(coin_records), len(all_coin_states))): + assert coin_records[i].coin.name().hex() == all_coin_states[i].coin.name().hex(), i + + # For the middle case, insert a coin record between the two heights 10 and 12. + await coin_store._add_coin_records( + [ + CoinRecord( + coin=Coin(std_hash(b"extra coin"), ph, 0), + # Insert a coin record in the middle between heights 10 and 12. + # Or after all of the other coins if testing the batch limit. + confirmed_block_index=uint32(11 if cut_off_middle else 50), + spent_block_index=uint32(0), + coinbase=False, + timestamp=uint64(0), + ) + ] + ) + + (all_coin_states, next_height) = await coin_store.batch_coin_states_by_puzzle_hashes([ph]) + + # Make sure that the extra coin records are not included in the results. + assert next_height == (12 if cut_off_middle else 50) + assert len(all_coin_states) == (25001 if cut_off_middle else 50000) + + @pytest.mark.anyio async def test_unsupported_version() -> None: with pytest.raises(RuntimeError, match="CoinStore does not support database schema v1"): diff --git a/tests/wallet/rpc/test_wallet_rpc.py b/tests/wallet/rpc/test_wallet_rpc.py index aeb87bae89..a4a8cdf6a8 100644 --- a/tests/wallet/rpc/test_wallet_rpc.py +++ b/tests/wallet/rpc/test_wallet_rpc.py @@ -16,6 +16,7 @@ from chia.consensus.block_rewards import calculate_base_farmer_reward, calculate from chia.consensus.coinbase import create_puzzlehash_for_pk from chia.rpc.full_node_rpc_client import FullNodeRpcClient from chia.rpc.rpc_server import RpcServer +from chia.rpc.wallet_request_types import GetNotifications from chia.rpc.wallet_rpc_api import WalletRpcApi from chia.rpc.wallet_rpc_client import WalletRpcClient from chia.server.server import ChiaServer @@ -2059,14 +2060,14 @@ async def test_notification_rpcs(wallet_rpc_environment: WalletRpcTestEnvironmen await farm_transaction(full_node_api, wallet_node, tx.spend_bundle) await time_out_assert(20, env.wallet_2.wallet.get_confirmed_balance, uint64(100000000000)) - notification = (await client_2.get_notifications())[0] - assert [notification] == (await client_2.get_notifications([notification.coin_id])) - assert [] == (await client_2.get_notifications(pagination=(0, 0))) - assert [notification] == (await client_2.get_notifications(pagination=(None, 1))) - assert [] == (await client_2.get_notifications(pagination=(1, None))) - assert [notification] == (await client_2.get_notifications(pagination=(None, None))) + notification = (await client_2.get_notifications(GetNotifications())).notifications[0] + assert [notification] == (await client_2.get_notifications(GetNotifications([notification.id]))).notifications + assert [] == (await client_2.get_notifications(GetNotifications(None, uint32(0), uint32(0)))).notifications + assert [notification] == (await client_2.get_notifications(GetNotifications(None, None, uint32(1)))).notifications + assert [] == (await client_2.get_notifications(GetNotifications(None, uint32(1), None))).notifications + assert [notification] == (await client_2.get_notifications(GetNotifications(None, None, None))).notifications assert await client_2.delete_notifications() - assert [] == (await client_2.get_notifications([notification.coin_id])) + assert [] == (await client_2.get_notifications(GetNotifications([notification.id]))).notifications tx = await client.send_notification( await wallet_2.get_new_puzzlehash(), @@ -2085,9 +2086,9 @@ async def test_notification_rpcs(wallet_rpc_environment: WalletRpcTestEnvironmen await farm_transaction(full_node_api, wallet_node, tx.spend_bundle) await time_out_assert(20, env.wallet_2.wallet.get_confirmed_balance, uint64(200000000000)) - notification = (await client_2.get_notifications())[0] - assert await client_2.delete_notifications([notification.coin_id]) - assert [] == (await client_2.get_notifications([notification.coin_id])) + notification = (await client_2.get_notifications(GetNotifications())).notifications[0] + assert await client_2.delete_notifications([notification.id]) + assert [] == (await client_2.get_notifications(GetNotifications([notification.id]))).notifications # The signatures below were made from an ephemeral key pair that isn't included in the test code. diff --git a/tests/wallet/simple_sync/test_simple_sync_protocol.py b/tests/wallet/simple_sync/test_simple_sync_protocol.py index e79b78d51a..9fbd7b3943 100644 --- a/tests/wallet/simple_sync/test_simple_sync_protocol.py +++ b/tests/wallet/simple_sync/test_simple_sync_protocol.py @@ -1,8 +1,7 @@ -# flake8: noqa: F811, F401 from __future__ import annotations import asyncio -from typing import List, Optional +from typing import List import pytest from clvm.casts import int_to_bytes @@ -12,21 +11,17 @@ from chia.consensus.block_rewards import calculate_base_farmer_reward, calculate from chia.protocols import wallet_protocol from chia.protocols.full_node_protocol import RespondTransaction from chia.protocols.protocol_message_types import ProtocolMessageTypes -from chia.protocols.wallet_protocol import CoinStateUpdate, RespondToCoinUpdates, RespondToPhUpdates -from chia.server.outbound_message import NodeType +from chia.protocols.wallet_protocol import CoinStateUpdate, RespondToCoinUpdates +from chia.server.outbound_message import Message, NodeType from chia.simulator.simulator_protocol import FarmNewBlockProtocol, ReorgProtocol -from chia.simulator.wallet_tools import WalletTool from chia.types.blockchain_format.coin import Coin from chia.types.blockchain_format.sized_bytes import bytes32 -from chia.types.coin_record import CoinRecord from chia.types.condition_opcodes import ConditionOpcode from chia.types.condition_with_args import ConditionWithArgs from chia.types.peer_info import PeerInfo -from chia.types.spend_bundle import SpendBundle from chia.util.ints import uint32, uint64 from chia.wallet.util.tx_config import DEFAULT_TX_CONFIG from chia.wallet.wallet import Wallet -from chia.wallet.wallet_state_manager import WalletStateManager from tests.connection_utils import add_dummy_connection from tests.util.setup_nodes import OldSimulatorsAndWallets from tests.util.time_out_assert import time_out_assert @@ -36,7 +31,7 @@ log = getLogger(__name__) zero_ph = bytes32(32 * b"\0") -async def get_all_messages_in_queue(queue): +async def get_all_messages_in_queue(queue: asyncio.Queue[Message]) -> List[Message]: all_messages = [] await asyncio.sleep(2) while not queue.empty(): @@ -46,44 +41,43 @@ async def get_all_messages_in_queue(queue): @pytest.mark.anyio -async def test_subscribe_for_ph(simulator_and_wallet, self_hostname): +async def test_subscribe_for_ph(simulator_and_wallet: OldSimulatorsAndWallets, self_hostname: str) -> None: num_blocks = 4 full_nodes, wallets, _ = simulator_and_wallet full_node_api = full_nodes[0] wallet_node, server_2 = wallets[0] fn_server = full_node_api.full_node.server - wsm: WalletStateManager = wallet_node.wallet_state_manager await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None) incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET) - junk_ph = 32 * b"\a" + junk_ph = bytes32(32 * b"\a") fake_wallet_peer = fn_server.all_connections[peer_id] - msg = wallet_protocol.RegisterForPhUpdates([zero_ph], 0) + msg = wallet_protocol.RegisterForPhUpdates([zero_ph], uint32(0)) msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer) assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value - data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data) + data_response = RespondToCoinUpdates.from_bytes(msg_response.data) assert data_response.coin_states == [] # Farm few more with reward - for i in range(0, num_blocks): + for i in range(num_blocks): if i == num_blocks - 1: await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph)) await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph)) else: await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph)) - msg = wallet_protocol.RegisterForPhUpdates([zero_ph], 0) + msg = wallet_protocol.RegisterForPhUpdates([zero_ph], uint32(0)) msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer) assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value - data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data) + data_response = RespondToCoinUpdates.from_bytes(msg_response.data) # we have already subscribed to this puzzle hash, it will be ignored # we still receive the updates (see below) assert data_response.coin_states == [] # Farm more rewards to check the incoming queue for the updates - for i in range(0, num_blocks): + for i in range(num_blocks): if i == num_blocks - 1: await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph)) await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph)) @@ -98,35 +92,36 @@ async def test_subscribe_for_ph(simulator_and_wallet, self_hostname): for message in all_messages: if message.type == ProtocolMessageTypes.coin_state_update.value: - data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data) - for coin_state in data_response.items: + coin_state_update = CoinStateUpdate.from_bytes(message.data) + assert len(coin_state_update.items) == 2 # 2 per height farmer / pool reward + for coin_state in coin_state_update.items: notified_zero_coins.add(coin_state) - assert len(data_response.items) == 2 # 2 per height farmer / pool reward assert all_zero_coin == notified_zero_coins # Test subscribing to more coins - one_ph = 32 * b"\1" - msg = wallet_protocol.RegisterForPhUpdates([one_ph], 0) + one_ph = bytes32(32 * b"\1") + msg = wallet_protocol.RegisterForPhUpdates([one_ph], uint32(0)) msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer) peak = full_node_api.full_node.blockchain.get_peak() - for i in range(0, num_blocks): + for i in range(num_blocks): if i == num_blocks - 1: await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph)) await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph)) else: await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph)) - for i in range(0, num_blocks): + for i in range(num_blocks): if i == num_blocks - 1: await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(one_ph)) await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph)) else: await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(one_ph)) + assert peak is not None zero_coins = await full_node_api.full_node.coin_store.get_coin_states_by_puzzle_hashes( - True, {zero_ph}, peak.height + 1 + True, {zero_ph}, uint32(peak.height + 1) ) one_coins = await full_node_api.full_node.coin_store.get_coin_states_by_puzzle_hashes(True, {one_ph}) @@ -139,18 +134,18 @@ async def test_subscribe_for_ph(simulator_and_wallet, self_hostname): for message in all_messages: if message.type == ProtocolMessageTypes.coin_state_update.value: - data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data) - for coin_state in data_response.items: + coin_state_update = CoinStateUpdate.from_bytes(message.data) + assert len(coin_state_update.items) == 2 # 2 per height farmer / pool reward + for coin_state in coin_state_update.items: notified_all_coins.add(coin_state) - assert len(data_response.items) == 2 # 2 per height farmer / pool reward assert all_coins == notified_all_coins - wsm: WalletStateManager = wallet_node.wallet_state_manager - wallet: Wallet = wsm.wallets[1] + wallet = wallet_node.wallet_state_manager.wallets[uint32(1)] + assert isinstance(wallet, Wallet) puzzle_hash = await wallet.get_new_puzzlehash() - for i in range(0, num_blocks): + for i in range(num_blocks): if i == num_blocks - 1: await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash)) await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph)) @@ -168,15 +163,16 @@ async def test_subscribe_for_ph(simulator_and_wallet, self_hostname): await time_out_assert(20, wallet.get_confirmed_balance, funds) assert funds == fn_amount - msg_1 = wallet_protocol.RegisterForPhUpdates([puzzle_hash], 0) + msg_1 = wallet_protocol.RegisterForPhUpdates([puzzle_hash], uint32(0)) msg_response_1 = await full_node_api.register_interest_in_puzzle_hash(msg_1, fake_wallet_peer) assert msg_response_1.type == ProtocolMessageTypes.respond_to_ph_update.value - data_response_1: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response_1.data) + data_response_1 = RespondToCoinUpdates.from_bytes(msg_response_1.data) assert len(data_response_1.coin_states) == 2 * num_blocks # 2 per height farmer / pool reward await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20) [tx_record] = await wallet.generate_signed_transaction(uint64(10), puzzle_hash, DEFAULT_TX_CONFIG, uint64(0)) + assert tx_record.spend_bundle is not None assert len(tx_record.spend_bundle.removals()) == 1 spent_coin = tx_record.spend_bundle.removals()[0] assert spent_coin.puzzle_hash == puzzle_hash @@ -211,8 +207,8 @@ async def test_subscribe_for_ph(simulator_and_wallet, self_hostname): for message in all_messages: if message.type == ProtocolMessageTypes.coin_state_update.value: - data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data) - for coin_state in data_response.items: + coin_state_update = CoinStateUpdate.from_bytes(message.data) + for coin_state in coin_state_update.items: if coin_state.coin.name() == spent_coin.name(): notified_state = coin_state @@ -222,14 +218,14 @@ async def test_subscribe_for_ph(simulator_and_wallet, self_hostname): @pytest.mark.anyio -async def test_subscribe_for_coin_id(simulator_and_wallet, self_hostname): +async def test_subscribe_for_coin_id(simulator_and_wallet: OldSimulatorsAndWallets, self_hostname: str) -> None: num_blocks = 4 full_nodes, wallets, _ = simulator_and_wallet full_node_api = full_nodes[0] wallet_node, server_2 = wallets[0] fn_server = full_node_api.full_node.server - wsm: WalletStateManager = wallet_node.wallet_state_manager - standard_wallet: Wallet = wsm.wallets[1] + standard_wallet = wallet_node.wallet_state_manager.wallets[uint32(1)] + assert isinstance(standard_wallet, Wallet) puzzle_hash = await standard_wallet.get_new_puzzlehash() await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None) @@ -238,7 +234,7 @@ async def test_subscribe_for_coin_id(simulator_and_wallet, self_hostname): fake_wallet_peer = fn_server.all_connections[peer_id] # Farm to create a coin that we'll track - for i in range(0, num_blocks): + for _ in range(num_blocks): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash)) funds = sum( @@ -247,16 +243,14 @@ async def test_subscribe_for_coin_id(simulator_and_wallet, self_hostname): await time_out_assert(20, standard_wallet.get_confirmed_balance, funds) - my_coins: List[CoinRecord] = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash( - True, puzzle_hash - ) + my_coins = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(True, puzzle_hash) coin_to_spend = my_coins[0].coin - msg = wallet_protocol.RegisterForCoinUpdates([coin_to_spend.name()], 0) + msg = wallet_protocol.RegisterForCoinUpdates([coin_to_spend.name()], uint32(0)) msg_response = await full_node_api.register_interest_in_coin(msg, fake_wallet_peer) assert msg_response is not None assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value - data_response: RespondToCoinUpdates = RespondToCoinUpdates.from_bytes(msg_response.data) + data_response = RespondToCoinUpdates.from_bytes(msg_response.data) assert data_response.coin_states[0].coin == coin_to_spend coins = set() @@ -273,8 +267,8 @@ async def test_subscribe_for_coin_id(simulator_and_wallet, self_hostname): notified_coins = set() for message in all_messages: if message.type == ProtocolMessageTypes.coin_state_update.value: - data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data) - for coin_state in data_response.items: + coin_state_update = CoinStateUpdate.from_bytes(message.data) + for coin_state in coin_state_update.items: notified_coins.add(coin_state.coin) assert coin_state.spent_height is not None @@ -287,18 +281,19 @@ async def test_subscribe_for_coin_id(simulator_and_wallet, self_hostname): uint64(10), puzzle_hash, DEFAULT_TX_CONFIG, uint64(0) ) - added_target: Optional[Coin] = None + added_target = None + assert tx_record.spend_bundle is not None for coin in tx_record.spend_bundle.additions(): if coin.puzzle_hash == puzzle_hash: added_target = coin assert added_target is not None - msg = wallet_protocol.RegisterForCoinUpdates([added_target.name()], 0) + msg = wallet_protocol.RegisterForCoinUpdates([added_target.name()], uint32(0)) msg_response = await full_node_api.register_interest_in_coin(msg, fake_wallet_peer) assert msg_response is not None assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value - data_response: RespondToCoinUpdates = RespondToCoinUpdates.from_bytes(msg_response.data) + data_response = RespondToCoinUpdates.from_bytes(msg_response.data) assert len(data_response.coin_states) == 0 await standard_wallet.push_transaction(tx_record) @@ -311,8 +306,8 @@ async def test_subscribe_for_coin_id(simulator_and_wallet, self_hostname): for message in all_messages: if message.type == ProtocolMessageTypes.coin_state_update.value: - data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data) - for coin_state in data_response.items: + coin_state_update = CoinStateUpdate.from_bytes(message.data) + for coin_state in coin_state_update.items: if coin_state.coin.name() == added_target.name(): notified_state = coin_state @@ -322,15 +317,15 @@ async def test_subscribe_for_coin_id(simulator_and_wallet, self_hostname): @pytest.mark.anyio -async def test_subscribe_for_ph_reorg(simulator_and_wallet, self_hostname): +async def test_subscribe_for_ph_reorg(simulator_and_wallet: OldSimulatorsAndWallets, self_hostname: str) -> None: num_blocks = 4 long_blocks = 20 full_nodes, wallets, _ = simulator_and_wallet full_node_api = full_nodes[0] wallet_node, server_2 = wallets[0] fn_server = full_node_api.full_node.server - wsm: WalletStateManager = wallet_node.wallet_state_manager - standard_wallet: Wallet = wsm.wallets[1] + standard_wallet = wallet_node.wallet_state_manager.wallets[uint32(1)] + assert isinstance(standard_wallet, Wallet) puzzle_hash = await standard_wallet.get_new_puzzlehash() await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None) @@ -339,18 +334,18 @@ async def test_subscribe_for_ph_reorg(simulator_and_wallet, self_hostname): fake_wallet_peer = fn_server.all_connections[peer_id] # Farm to create a coin that we'll track - for i in range(0, num_blocks): + for _ in range(num_blocks): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph)) - for i in range(0, long_blocks): + for _ in range(long_blocks): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph)) - msg = wallet_protocol.RegisterForPhUpdates([puzzle_hash], 0) + msg = wallet_protocol.RegisterForPhUpdates([puzzle_hash], uint32(0)) msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer) assert msg_response is not None await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash)) - for i in range(0, num_blocks): + for _ in range(num_blocks): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph)) expected_height = uint32(long_blocks + 2 * num_blocks + 1) @@ -358,8 +353,8 @@ async def test_subscribe_for_ph_reorg(simulator_and_wallet, self_hostname): coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(True, puzzle_hash) assert len(coin_records) > 0 - fork_height = expected_height - num_blocks - 5 - req = ReorgProtocol(fork_height, expected_height + 5, zero_ph, None) + fork_height = uint32(expected_height - num_blocks - 5) + req = ReorgProtocol(fork_height, uint32(expected_height + 5), zero_ph, None) await full_node_api.reorg_from_index_to_new_index(req) coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(True, puzzle_hash) @@ -367,11 +362,11 @@ async def test_subscribe_for_ph_reorg(simulator_and_wallet, self_hostname): all_messages = await get_all_messages_in_queue(incoming_queue) - coin_update_messages = [] + coin_update_messages: List[CoinStateUpdate] = [] for message in all_messages: if message.type == ProtocolMessageTypes.coin_state_update.value: - data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data) - coin_update_messages.append(data_response) + coin_state_update = CoinStateUpdate.from_bytes(message.data) + coin_update_messages.append(coin_state_update) # First state is creation, second one is a reorg assert len(coin_update_messages) == 2 @@ -397,15 +392,15 @@ async def test_subscribe_for_ph_reorg(simulator_and_wallet, self_hostname): @pytest.mark.anyio -async def test_subscribe_for_coin_id_reorg(simulator_and_wallet, self_hostname): +async def test_subscribe_for_coin_id_reorg(simulator_and_wallet: OldSimulatorsAndWallets, self_hostname: str) -> None: num_blocks = 4 long_blocks = 20 full_nodes, wallets, _ = simulator_and_wallet full_node_api = full_nodes[0] wallet_node, server_2 = wallets[0] fn_server = full_node_api.full_node.server - wsm: WalletStateManager = wallet_node.wallet_state_manager - standard_wallet: Wallet = wsm.wallets[1] + standard_wallet = wallet_node.wallet_state_manager.wallets[uint32(1)] + assert isinstance(standard_wallet, Wallet) puzzle_hash = await standard_wallet.get_new_puzzlehash() await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None) @@ -414,15 +409,15 @@ async def test_subscribe_for_coin_id_reorg(simulator_and_wallet, self_hostname): fake_wallet_peer = fn_server.all_connections[peer_id] # Farm to create a coin that we'll track - for i in range(0, num_blocks): + for _ in range(num_blocks): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph)) - for i in range(0, long_blocks): + for _ in range(long_blocks): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph)) await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash)) - for i in range(0, num_blocks): + for _ in range(num_blocks): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph)) expected_height = uint32(long_blocks + 2 * num_blocks + 1) @@ -432,12 +427,12 @@ async def test_subscribe_for_coin_id_reorg(simulator_and_wallet, self_hostname): assert len(coin_records) > 0 for coin_rec in coin_records: - msg = wallet_protocol.RegisterForCoinUpdates([coin_rec.name], 0) + msg = wallet_protocol.RegisterForCoinUpdates([coin_rec.name], uint32(0)) msg_response = await full_node_api.register_interest_in_coin(msg, fake_wallet_peer) assert msg_response is not None - fork_height = expected_height - num_blocks - 5 - req = ReorgProtocol(fork_height, expected_height + 5, zero_ph, None) + fork_height = uint32(expected_height - num_blocks - 5) + req = ReorgProtocol(fork_height, uint32(expected_height + 5), zero_ph, None) await full_node_api.reorg_from_index_to_new_index(req) coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(True, puzzle_hash) @@ -445,11 +440,11 @@ async def test_subscribe_for_coin_id_reorg(simulator_and_wallet, self_hostname): all_messages = await get_all_messages_in_queue(incoming_queue) - coin_update_messages = [] + coin_update_messages: List[CoinStateUpdate] = [] for message in all_messages: if message.type == ProtocolMessageTypes.coin_state_update.value: - data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data) - coin_update_messages.append(data_response) + coin_state_update = CoinStateUpdate.from_bytes(message.data) + coin_update_messages.append(coin_state_update) assert len(coin_update_messages) == 1 update = coin_update_messages[0] @@ -464,20 +459,19 @@ async def test_subscribe_for_coin_id_reorg(simulator_and_wallet, self_hostname): @pytest.mark.anyio -async def test_subscribe_for_hint(simulator_and_wallet, self_hostname): +async def test_subscribe_for_hint(simulator_and_wallet: OldSimulatorsAndWallets, self_hostname: str) -> None: num_blocks = 4 full_nodes, wallets, bt = simulator_and_wallet full_node_api = full_nodes[0] wallet_node, server_2 = wallets[0] fn_server = full_node_api.full_node.server - wsm: WalletStateManager = wallet_node.wallet_state_manager await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None) incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET) - wt: WalletTool = bt.get_pool_wallet_tool() + wt = bt.get_pool_wallet_tool() ph = wt.get_new_puzzlehash() - for i in range(0, num_blocks): + for _ in range(num_blocks): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph)) await asyncio.sleep(6) @@ -486,13 +480,13 @@ async def test_subscribe_for_hint(simulator_and_wallet, self_hostname): hint_puzzle_hash = 32 * b"\2" amount = 1 amount_bin = int_to_bytes(1) - hint = 32 * b"\5" + hint = bytes32(32 * b"\5") fake_wallet_peer = fn_server.all_connections[peer_id] - msg = wallet_protocol.RegisterForPhUpdates([hint], 0) + msg = wallet_protocol.RegisterForPhUpdates([hint], uint32(0)) msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer) assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value - data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data) + data_response = RespondToCoinUpdates.from_bytes(msg_response.data) assert len(data_response.coin_states) == 0 condition_dict = { @@ -502,12 +496,7 @@ async def test_subscribe_for_hint(simulator_and_wallet, self_hostname): } await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20) - tx: SpendBundle = wt.generate_signed_transaction( - 10, - wt.get_new_puzzlehash(), - coin_spent, - condition_dic=condition_dict, - ) + tx = wt.generate_signed_transaction(uint64(10), wt.get_new_puzzlehash(), coin_spent, condition_dic=condition_dict) await full_node_api.respond_transaction(RespondTransaction(tx), fake_wallet_peer) await full_node_api.process_spend_bundles(bundles=[tx]) @@ -518,20 +507,20 @@ async def test_subscribe_for_hint(simulator_and_wallet, self_hostname): for message in all_messages: if message.type == ProtocolMessageTypes.coin_state_update.value: - data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data) - notified_state = data_response + coin_state_update = CoinStateUpdate.from_bytes(message.data) + notified_state = coin_state_update break assert notified_state is not None assert notified_state.items[0].coin == Coin(coin_spent.name(), hint_puzzle_hash, amount) - msg = wallet_protocol.RegisterForPhUpdates([hint], 0) + msg = wallet_protocol.RegisterForPhUpdates([hint], uint32(0)) msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer) assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value - data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data) + response = RespondToCoinUpdates.from_bytes(msg_response.data) # we have already subscribed to this puzzle hash. The full node will # ignore the duplicate - assert data_response.coin_states == [] + assert response.coin_states == [] @pytest.mark.anyio @@ -543,14 +532,14 @@ async def test_subscribe_for_puzzle_hash_coin_hint_duplicates( await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None) - wt: WalletTool = bt.get_pool_wallet_tool() + wt = bt.get_pool_wallet_tool() ph = wt.get_new_puzzlehash() await full_node_api.farm_blocks_to_puzzlehash(4, ph) coins = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hashes(False, [ph]) wallet_connection = full_node_server.all_connections[wallet_server.node_id] # Create a coin which is hinted with its own destination puzzle hash - tx: SpendBundle = wt.generate_signed_transaction( + tx = wt.generate_signed_transaction( uint64(10), wt.get_new_puzzlehash(), coins[0].coin, @@ -570,25 +559,24 @@ async def test_subscribe_for_puzzle_hash_coin_hint_duplicates( @pytest.mark.anyio -async def test_subscribe_for_hint_long_sync(wallet_two_node_simulator, self_hostname): +async def test_subscribe_for_hint_long_sync( + wallet_two_node_simulator: OldSimulatorsAndWallets, self_hostname: str +) -> None: num_blocks = 4 full_nodes, wallets, bt = wallet_two_node_simulator full_node_api = full_nodes[0] full_node_api_1 = full_nodes[1] - wallet_node, server_2 = wallets[0] fn_server = full_node_api.full_node.server fn_server_1 = full_node_api_1.full_node.server - wsm: WalletStateManager = wallet_node.wallet_state_manager - await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None) incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET) incoming_queue_1, peer_id_1 = await add_dummy_connection(fn_server_1, self_hostname, 12313, NodeType.WALLET) - wt: WalletTool = bt.get_pool_wallet_tool() + wt = bt.get_pool_wallet_tool() ph = wt.get_new_puzzlehash() - for i in range(0, num_blocks): + for _ in range(num_blocks): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph)) await asyncio.sleep(6) @@ -597,16 +585,16 @@ async def test_subscribe_for_hint_long_sync(wallet_two_node_simulator, self_host hint_puzzle_hash = 32 * b"\2" amount = 1 amount_bin = int_to_bytes(1) - hint = 32 * b"\5" + hint = bytes32(32 * b"\5") fake_wallet_peer = fn_server.all_connections[peer_id] fake_wallet_peer_1 = fn_server_1.all_connections[peer_id_1] - msg = wallet_protocol.RegisterForPhUpdates([hint], 0) + msg = wallet_protocol.RegisterForPhUpdates([hint], uint32(0)) msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer) - msg_response_1 = await full_node_api_1.register_interest_in_puzzle_hash(msg, fake_wallet_peer_1) + await full_node_api_1.register_interest_in_puzzle_hash(msg, fake_wallet_peer_1) assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value - data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data) + data_response = RespondToCoinUpdates.from_bytes(msg_response.data) assert len(data_response.coin_states) == 0 condition_dict = { @@ -616,18 +604,13 @@ async def test_subscribe_for_hint_long_sync(wallet_two_node_simulator, self_host } await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20) - tx: SpendBundle = wt.generate_signed_transaction( - 10, - wt.get_new_puzzlehash(), - coin_spent, - condition_dic=condition_dict, - ) + tx = wt.generate_signed_transaction(uint64(10), wt.get_new_puzzlehash(), coin_spent, condition_dic=condition_dict) await full_node_api.respond_transaction(RespondTransaction(tx), fake_wallet_peer) await full_node_api.process_spend_bundles(bundles=[tx]) # Create more blocks than recent "short_sync_blocks_behind_threshold" so that node enters batch - for i in range(0, 100): + for _ in range(100): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph)) node1_height = full_node_api_1.full_node.blockchain.get_peak_height() @@ -640,13 +623,13 @@ async def test_subscribe_for_hint_long_sync(wallet_two_node_simulator, self_host all_messages = await get_all_messages_in_queue(incoming_queue) all_messages_1 = await get_all_messages_in_queue(incoming_queue_1) - def check_messages_for_hint(messages): + def check_messages_for_hint(messages: List[Message]) -> None: notified_state = None for message in messages: if message.type == ProtocolMessageTypes.coin_state_update.value: - data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data) - notified_state = data_response + coin_state_update = CoinStateUpdate.from_bytes(message.data) + notified_state = coin_state_update break assert notified_state is not None @@ -657,24 +640,24 @@ async def test_subscribe_for_hint_long_sync(wallet_two_node_simulator, self_host @pytest.mark.anyio -async def test_ph_subscribe_limits(simulator_and_wallet, self_hostname): +async def test_ph_subscribe_limits(simulator_and_wallet: OldSimulatorsAndWallets, self_hostname: str) -> None: full_nodes, wallets, _ = simulator_and_wallet full_node_api = full_nodes[0] - wallet_node, server_2 = wallets[0] + _, server_2 = wallets[0] fn_server = full_node_api.full_node.server await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None) con = list(fn_server.all_connections.values())[0] phs = [] - phs.append(32 * b"\0") - phs.append(32 * b"\1") - phs.append(32 * b"\2") - phs.append(32 * b"\3") - phs.append(32 * b"\4") - phs.append(32 * b"\5") - phs.append(32 * b"\6") + phs.append(bytes32(32 * b"\0")) + phs.append(bytes32(32 * b"\1")) + phs.append(bytes32(32 * b"\2")) + phs.append(bytes32(32 * b"\3")) + phs.append(bytes32(32 * b"\4")) + phs.append(bytes32(32 * b"\5")) + phs.append(bytes32(32 * b"\6")) full_node_api.full_node.config["max_subscribe_items"] = 2 assert full_node_api.is_trusted(con) is False - msg = wallet_protocol.RegisterForPhUpdates(phs, 0) + msg = wallet_protocol.RegisterForPhUpdates(phs, uint32(0)) msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, con) assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value s = full_node_api.full_node.subscriptions @@ -698,24 +681,24 @@ async def test_ph_subscribe_limits(simulator_and_wallet, self_hostname): @pytest.mark.anyio -async def test_coin_subscribe_limits(simulator_and_wallet, self_hostname): +async def test_coin_subscribe_limits(simulator_and_wallet: OldSimulatorsAndWallets, self_hostname: str) -> None: full_nodes, wallets, _ = simulator_and_wallet full_node_api = full_nodes[0] - wallet_node, server_2 = wallets[0] + _, server_2 = wallets[0] fn_server = full_node_api.full_node.server await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None) con = list(fn_server.all_connections.values())[0] coins = [] - coins.append(32 * b"\0") - coins.append(32 * b"\1") - coins.append(32 * b"\2") - coins.append(32 * b"\3") - coins.append(32 * b"\4") - coins.append(32 * b"\5") - coins.append(32 * b"\6") + coins.append(bytes32(32 * b"\0")) + coins.append(bytes32(32 * b"\1")) + coins.append(bytes32(32 * b"\2")) + coins.append(bytes32(32 * b"\3")) + coins.append(bytes32(32 * b"\4")) + coins.append(bytes32(32 * b"\5")) + coins.append(bytes32(32 * b"\6")) full_node_api.full_node.config["max_subscribe_items"] = 2 assert full_node_api.is_trusted(con) is False - msg = wallet_protocol.RegisterForCoinUpdates(coins, 0) + msg = wallet_protocol.RegisterForCoinUpdates(coins, uint32(0)) msg_response = await full_node_api.register_interest_in_coin(msg, con) assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value s = full_node_api.full_node.subscriptions diff --git a/tests/wallet/test_notifications.py b/tests/wallet/test_notifications.py index f4446576fd..5c328bdf24 100644 --- a/tests/wallet/test_notifications.py +++ b/tests/wallet/test_notifications.py @@ -166,7 +166,7 @@ async def test_notifications( assert len(notifications) == 1 assert notifications[0].message == b"allow_larger" assert ( - await notification_manager_2.notification_store.get_notifications([n.coin_id for n in notifications]) + await notification_manager_2.notification_store.get_notifications([n.id for n in notifications]) == notifications ) @@ -176,7 +176,7 @@ async def test_notifications( await notification_manager_2.notification_store.delete_all_notifications() assert len(await notification_manager_2.notification_store.get_all_notifications()) == 0 await notification_manager_2.notification_store.add_notification(notifications[0]) - await notification_manager_2.notification_store.delete_notifications([n.coin_id for n in notifications]) + await notification_manager_2.notification_store.delete_notifications([n.id for n in notifications]) assert len(await notification_manager_2.notification_store.get_all_notifications()) == 0 assert not await func(*notification_manager_2.most_recent_args)