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