Merge remote-tracking branch 'origin/main' into quex.remove_announcement_main

This commit is contained in:
Matt
2024-01-23 12:17:31 -08:00
21 changed files with 610 additions and 267 deletions
+6 -3
View File
@@ -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}")
+9 -1
View File
@@ -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(
+27
View File
@@ -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(
+91
View File
@@ -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
+3 -4
View File
@@ -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:
+5
View File
@@ -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 []
+23
View File
@@ -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]
+9 -25
View File
@@ -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)
+3 -22
View File
@@ -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 = {}
+2 -1
View File
@@ -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,
+2
View File
@@ -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
+10 -40
View File
@@ -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]:
+17 -8
View File
@@ -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(
+2 -7
View File
@@ -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
-2
View File
@@ -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
+73
View File
@@ -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
+7 -6
View File
@@ -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)
@@ -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"):
+11 -10
View File
@@ -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.
@@ -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
+2 -2
View File
@@ -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)