11from __future__ import annotations
22
33import asyncio
4+ import copy
45import logging
56import time
67import traceback
4445from chia .full_node .coin_store import CoinStore
4546from chia .full_node .fee_estimator_interface import FeeEstimatorInterface
4647from chia .full_node .full_block_utils import get_height_and_tx_status_from_block , header_block_from_block
47- from chia .full_node .tx_processing_queue import TransactionQueueEntry , TransactionQueueFull
48+ from chia .full_node .tx_processing_queue import PeerWithTx , TransactionQueueEntry , TransactionQueueFull
4849from chia .protocols import farmer_protocol , full_node_protocol , introducer_protocol , timelord_protocol , wallet_protocol
4950from chia .protocols .fee_estimate import FeeEstimate , FeeEstimateGroup , fee_rate_v2_to_v1
5051from chia .protocols .full_node_protocol import RejectBlock , RejectBlocks
5152from chia .protocols .outbound_message import Message , make_msg
5253from chia .protocols .protocol_message_types import ProtocolMessageTypes
53- from chia .protocols .protocol_timing import RATE_LIMITER_BAN_SECONDS
54+ from chia .protocols .protocol_timing import CONSENSUS_ERROR_BAN_SECONDS , RATE_LIMITER_BAN_SECONDS
5455from chia .protocols .shared_protocol import Capability
5556from chia .protocols .wallet_protocol import (
5657 PuzzleSolutionResponse ,
@@ -89,6 +90,10 @@ async def tx_request_and_timeout(full_node: FullNode, transaction_id: bytes32, t
8990 receive it or timeout.
9091 """
9192 counter = 0
93+ # Make a copy as we'll pop from it here. We keep the original intact as we
94+ # need it in `respond_transaction` when constructing `TransactionQueueEntry`
95+ # to put into `transaction_queue`.
96+ peers_with_tx = copy .copy (full_node .full_node_store .peers_with_tx .get (transaction_id , {}))
9297 try :
9398 while True :
9499 # Limit to asking a few peers, it's possible that this tx got included on chain already
@@ -98,10 +103,9 @@ async def tx_request_and_timeout(full_node: FullNode, transaction_id: bytes32, t
98103 break
99104 if transaction_id not in full_node .full_node_store .peers_with_tx :
100105 break
101- peers_with_tx : set [bytes32 ] = full_node .full_node_store .peers_with_tx [transaction_id ]
102106 if len (peers_with_tx ) == 0 :
103107 break
104- peer_id = peers_with_tx .pop ()
108+ peer_id , _ = peers_with_tx .popitem ()
105109 assert full_node .server is not None
106110 if peer_id not in full_node .server .all_connections :
107111 continue
@@ -221,28 +225,44 @@ async def new_transaction(
221225 f"with mismatch on cost { transaction .cost } vs validation cost { mempool_item .cost } and/or "
222226 f"fee { transaction .fees } vs { mempool_item .fee } ."
223227 )
224- await peer .close (RATE_LIMITER_BAN_SECONDS )
228+ await peer .close (CONSENSUS_ERROR_BAN_SECONDS )
225229 return None
226230
227231 if self .full_node .mempool_manager .is_fee_enough (transaction .fees , transaction .cost ):
228232 # If there's current pending request just add this peer to the set of peers that have this tx
229233 if transaction .transaction_id in self .full_node .full_node_store .pending_tx_request :
230- if transaction .transaction_id in self .full_node .full_node_store .peers_with_tx :
231- current_set = self .full_node .full_node_store .peers_with_tx [transaction .transaction_id ]
232- if peer .peer_node_id in current_set :
233- return None
234- current_set .add (peer .peer_node_id )
234+ current_map = self .full_node .full_node_store .peers_with_tx .get (transaction .transaction_id )
235+ if current_map is None :
236+ self .full_node .full_node_store .peers_with_tx [transaction .transaction_id ] = {
237+ peer .peer_node_id : PeerWithTx (
238+ peer_host = peer .peer_info .host ,
239+ advertised_fee = transaction .fees ,
240+ advertised_cost = transaction .cost ,
241+ )
242+ }
235243 return None
236- else :
237- new_set = set ()
238- new_set .add (peer .peer_node_id )
239- self .full_node .full_node_store .peers_with_tx [transaction .transaction_id ] = new_set
244+ prev = current_map .get (peer .peer_node_id )
245+ if prev is not None :
246+ if prev .advertised_fee != transaction .fees or prev .advertised_cost != transaction .cost :
247+ self .log .warning (
248+ f"Banning peer { peer .peer_node_id } . Sent us a new tx { transaction .transaction_id } with "
249+ f"mismatch on cost { transaction .cost } vs previous advertised cost { prev .advertised_cost } "
250+ f"and/or fee { transaction .fees } vs previous advertised fee { prev .advertised_fee } ."
251+ )
252+ await peer .close (CONSENSUS_ERROR_BAN_SECONDS )
240253 return None
254+ current_map [peer .peer_node_id ] = PeerWithTx (
255+ peer_host = peer .peer_info .host , advertised_fee = transaction .fees , advertised_cost = transaction .cost
256+ )
257+ return None
241258
242259 self .full_node .full_node_store .pending_tx_request [transaction .transaction_id ] = peer .peer_node_id
243- new_set = set ()
244- new_set .add (peer .peer_node_id )
245- self .full_node .full_node_store .peers_with_tx [transaction .transaction_id ] = new_set
260+ self .full_node .full_node_store .peers_with_tx [transaction .transaction_id ] = {
261+ peer .peer_node_id : PeerWithTx (
262+ peer_host = peer .peer_info .host , advertised_fee = transaction .fees , advertised_cost = transaction .cost
263+ )
264+ }
265+
246266 task_id : bytes32 = bytes32 .secret ()
247267 fetch_task = create_referenced_task (
248268 tx_request_and_timeout (self .full_node , transaction .transaction_id , task_id )
@@ -282,13 +302,15 @@ async def respond_transaction(
282302 spend_name = std_hash (tx_bytes )
283303 if spend_name in self .full_node .full_node_store .pending_tx_request :
284304 self .full_node .full_node_store .pending_tx_request .pop (spend_name )
305+ peers_with_tx = {}
285306 if spend_name in self .full_node .full_node_store .peers_with_tx :
286- self .full_node .full_node_store .peers_with_tx .pop (spend_name )
307+ peers_with_tx = self .full_node .full_node_store .peers_with_tx .pop (spend_name )
287308
288309 # TODO: Use fee in priority calculation, to prioritize high fee TXs
289310 try :
290311 await self .full_node .transaction_queue .put (
291- TransactionQueueEntry (tx .transaction , tx_bytes , spend_name , peer , test ), peer .peer_node_id
312+ TransactionQueueEntry (tx .transaction , tx_bytes , spend_name , peer , test , peers_with_tx ),
313+ peer .peer_node_id ,
292314 )
293315 except TransactionQueueFull :
294316 pass # we can't do anything here, the tx will be dropped. We might do something in the future.
0 commit comments