Add request_transaction and respond_transaction to rate limits v3.

This commit is contained in:
Amine Khaldi
2026-08-20 14:28:13 +01:00
parent 33c8674fda
commit eb44a9ea31
7 changed files with 25 additions and 11 deletions
+5 -5
View File
@@ -1213,7 +1213,7 @@ async def test_new_transaction_and_mempool(
await full_node_1.send_transaction(respond_transaction, fake_peer)
request = fnp.RequestTransaction(spend_bundle.get_hash())
req = await full_node_1.request_transaction(request)
req = await full_node_1.request_transaction(request, fake_peer)
fee_rate_for_med = full_node_1.full_node.mempool_manager.mempool.get_min_fee_rate(5000000)
fee_rate_for_large = full_node_1.full_node.mempool_manager.mempool.get_min_fee_rate(50000000)
@@ -1329,7 +1329,7 @@ async def test_request_respond_transaction(
tx_id = bytes32.random(seeded_random)
request_transaction = fnp.RequestTransaction(tx_id)
msg = await full_node_1.request_transaction(request_transaction)
msg = await full_node_1.request_transaction(request_transaction, peer)
assert msg is None
receiver_puzzlehash = wallet_receiver.get_new_puzzlehash()
@@ -1346,7 +1346,7 @@ async def test_request_respond_transaction(
await time_out_assert(10, time_out_messages(incoming_queue, "new_transaction"))
request_transaction = fnp.RequestTransaction(spend_bundle.get_hash())
msg = await full_node_1.request_transaction(request_transaction)
msg = await full_node_1.request_transaction(request_transaction, peer)
assert msg is not None
assert msg.data == bytes(fnp.RespondTransaction(spend_bundle))
@@ -1368,7 +1368,7 @@ async def test_respond_transaction_fail(
tx_id = bytes32.random(seeded_random)
request_transaction = fnp.RequestTransaction(tx_id)
msg = await full_node_1.request_transaction(request_transaction)
msg = await full_node_1.request_transaction(request_transaction, peer)
assert msg is None
receiver_puzzlehash = wallet_receiver.get_new_puzzlehash()
@@ -1617,7 +1617,7 @@ async def test_malformed_peer_version_on_connect(
await full_node_1.full_node.on_connect(peer)
# Unparseable version should be treated as old, so the counter is incremented
assert peer.expected_mempool_responses == 100
assert peer.expected_mempool_responses == 200
finally:
full_node_1.full_node.config["selected_network"] = original_network
@@ -113,7 +113,7 @@ class TestPerformance:
await full_node_1.respond_transaction(respond_transaction, fake_peer)
request_transaction = fnp.RequestTransaction(spend_bundle_id)
req = await full_node_1.request_transaction(request_transaction)
req = await full_node_1.request_transaction(request_transaction, fake_peer)
if req is None:
break
@@ -271,6 +271,7 @@ async def test_unsolicited_unlimited_v3_messages(
ProtocolMessageTypes.respond_proof_of_weight: network_protocol_data.respond_proof_of_weight,
ProtocolMessageTypes.respond_puzzle_solution: network_protocol_data.respond_puzzle_solution,
ProtocolMessageTypes.reject_puzzle_solution: network_protocol_data.reject_puzzle_solution,
ProtocolMessageTypes.respond_transaction: network_protocol_data.respond_transaction,
}
expected_unlimited = {msg_type for msg_type, settings in rate_limits_v3.items() if settings.window_size is None}
current_unlimited = set(unsolicited_messages)
+4 -2
View File
@@ -63,8 +63,10 @@ class FullNodeApiStub(ApiProtocol, Protocol):
"""Handle new transaction from peer."""
...
@metadata.request(reply_types=[ProtocolMessageTypes.respond_transaction])
async def request_transaction(self, request: full_node_protocol.RequestTransaction) -> Message | None:
@metadata.request(reply_types=[ProtocolMessageTypes.respond_transaction], peer_required=True)
async def request_transaction(
self, request: full_node_protocol.RequestTransaction, peer: WSChiaConnection
) -> Message | None:
"""Handle transaction request."""
...
+1 -1
View File
@@ -1050,7 +1050,7 @@ class FullNode:
except Exception:
old_peer = True
if old_peer:
connection.expected_mempool_responses = 100
connection.expected_mempool_responses = 200
peak_full: FullBlock | None = await self.blockchain.get_full_peak()
+11 -2
View File
@@ -343,8 +343,10 @@ class FullNodeAPI:
return None
return None
@metadata.request(reply_types=[ProtocolMessageTypes.respond_transaction])
async def request_transaction(self, request: full_node_protocol.RequestTransaction) -> Message | None:
@metadata.request(reply_types=[ProtocolMessageTypes.respond_transaction], peer_required=True)
async def request_transaction(
self, request: full_node_protocol.RequestTransaction, peer: WSChiaConnection
) -> Message | None:
"""Peer has requested a full transaction from us."""
# Ignore if syncing
if self.full_node.sync_store.get_sync_mode():
@@ -353,6 +355,12 @@ class FullNodeAPI:
if spend_bundle is None:
return None
if not is_localhost(peer.peer_info.host) and not is_in_network(
peer.peer_info.host, self.full_node.server.exempt_peer_networks
):
# Pace successful replies. Coupled with a rate limits v3 receive
# window of 3, 0.5s per fetch is ~360 replies per minute.
await asyncio.sleep(0.5)
transaction = full_node_protocol.RespondTransaction(spend_bundle)
msg = make_msg(ProtocolMessageTypes.respond_transaction, transaction)
@@ -381,6 +389,7 @@ class FullNodeAPI:
f"Received unsolicited transaction {spend_name} from peer "
f"{peer.peer_node_id} / {peer.peer_info.host} version {peer.version}"
)
await peer.close(RATE_LIMITER_BAN_SECONDS)
return None
peers_with_tx = {}
if spend_name in self.full_node.full_node_store.peers_with_tx:
+2
View File
@@ -68,6 +68,8 @@ rate_limits_v3: dict[ProtocolMessageTypes, RLSettingsV3] = {
ProtocolMessageTypes.request_puzzle_solution: RLSettingsV3(window_size=2),
ProtocolMessageTypes.respond_puzzle_solution: RLSettingsV3(window_size=None),
ProtocolMessageTypes.reject_puzzle_solution: RLSettingsV3(window_size=None),
ProtocolMessageTypes.request_transaction: RLSettingsV3(window_size=3),
ProtocolMessageTypes.respond_transaction: RLSettingsV3(window_size=None),
}
# Maximum number of window sizes we allow to be set by the