full_node: Don't send duplicates in register_interest_in_puzzle_hash (#15560)

* Test self hinted coin in puzzle hash subscription

* Don't send duplicates in `register_interest_in_puzzle_hash`

* Some tweaks in the test
This commit is contained in:
dustinface 2023-06-21 20:11:35 +02:00 committed by GitHub
parent c7e2acf219
commit f85e68ee22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 7 deletions

View File

@ -365,11 +365,11 @@ class CoinStore:
min_height: uint32 = uint32(0),
*,
max_items: int = 50000,
) -> List[CoinState]:
) -> Set[CoinState]:
if len(puzzle_hashes) == 0:
return []
return set()
coins: List[CoinState] = []
coins: Set[CoinState] = set()
async with self.db_wrapper.reader_no_transaction() as conn:
for batch in to_batches(puzzle_hashes, SQLITE_MAX_VARIABLE_NUMBER):
puzzle_hashes_db: Tuple[Any, ...]
@ -388,7 +388,7 @@ class CoinStore:
) as cursor:
row: sqlite3.Row
for row in await cursor.fetchall():
coins.append(self.row_to_coin_state(row))
coins.add(self.row_to_coin_state(row))
if len(coins) >= max_items:
break

View File

@ -1489,7 +1489,7 @@ class FullNodeAPI:
# before we send the response
# Send all coins with requested puzzle hash that have been created after the specified height
states: List[CoinState] = await self.full_node.coin_store.get_coin_states_by_puzzle_hashes(
states: Set[CoinState] = await self.full_node.coin_store.get_coin_states_by_puzzle_hashes(
include_spent_coins=True, puzzle_hashes=puzzle_hashes, min_height=request.min_height, max_items=max_items
)
max_items -= len(states)
@ -1511,7 +1511,7 @@ class FullNodeAPI:
min_height=request.min_height,
max_items=len(hint_coin_ids),
)
states.extend(hint_states)
states.update(hint_states)
end_time = time.monotonic()
@ -1530,7 +1530,7 @@ class FullNodeAPI:
end_time - start_time,
)
response = wallet_protocol.RespondToPhUpdates(request.puzzle_hashes, request.min_height, states)
response = wallet_protocol.RespondToPhUpdates(request.puzzle_hashes, request.min_height, list(states))
msg = make_msg(ProtocolMessageTypes.respond_to_ph_update, response)
return msg

View File

@ -14,6 +14,7 @@ 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.simulator.setup_nodes import SimulatorsAndWallets
from chia.simulator.simulator_protocol import FarmNewBlockProtocol, ReorgProtocol
from chia.simulator.time_out_assert import time_out_assert
from chia.simulator.wallet_tools import WalletTool
@ -535,6 +536,40 @@ class TestSimpleSyncProtocol:
# ignore the duplicate
assert data_response.coin_states == []
@pytest.mark.asyncio
async def test_subscribe_for_puzzle_hash_coin_hint_duplicates(
self, simulator_and_wallet: SimulatorsAndWallets, self_hostname: str
) -> None:
[full_node_api], [[_, wallet_server]], bt = simulator_and_wallet
full_node_server = full_node_api.full_node.server
await wallet_server.start_client(PeerInfo(self_hostname, uint16(full_node_server._port)), None)
wt: WalletTool = 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(
uint64(10),
wt.get_new_puzzlehash(),
coins[0].coin,
condition_dic={
ConditionOpcode.CREATE_COIN: [ConditionWithArgs(ConditionOpcode.CREATE_COIN, [ph, int_to_bytes(1), ph])]
},
)
await full_node_api.respond_transaction(RespondTransaction(tx), wallet_connection)
await full_node_api.process_spend_bundles(bundles=[tx])
# Query the coin states and make sure it doesn't contain duplicated entries
msg = wallet_protocol.RegisterForPhUpdates([ph], uint32(0))
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, wallet_connection)
assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
response = RespondToCoinUpdates.from_bytes(msg_response.data)
assert len(response.coin_states) > 0
assert len(set(response.coin_states)) == len(response.coin_states)
@pytest.mark.asyncio
async def test_subscribe_for_hint_long_sync(self, wallet_two_node_simulator, self_hostname):
num_blocks = 4