@@ -166,6 +166,8 @@ impl From<DecoderError> for PacketProcessError {
166
166
}
167
167
}
168
168
169
+ /// Version 65 of the Ethereum protocol and number of packet IDs reserved by the protocol (packet count).
170
+ pub const ETH_PROTOCOL_VERSION_65 : ( u8 , u8 ) = ( 65 , 0x11 ) ;
169
171
/// Version 64 of the Ethereum protocol and number of packet IDs reserved by the protocol (packet count).
170
172
pub const ETH_PROTOCOL_VERSION_64 : ( u8 , u8 ) = ( 64 , 0x11 ) ;
171
173
/// Version 63 of the Ethereum protocol and number of packet IDs reserved by the protocol (packet count).
@@ -217,6 +219,7 @@ const STATUS_TIMEOUT: Duration = Duration::from_secs(10);
217
219
const HEADERS_TIMEOUT : Duration = Duration :: from_secs ( 15 ) ;
218
220
const BODIES_TIMEOUT : Duration = Duration :: from_secs ( 20 ) ;
219
221
const RECEIPTS_TIMEOUT : Duration = Duration :: from_secs ( 10 ) ;
222
+ const POOLED_TRANSACTIONS_TIMEOUT : Duration = Duration :: from_secs ( 10 ) ;
220
223
const FORK_HEADER_TIMEOUT : Duration = Duration :: from_secs ( 3 ) ;
221
224
/// Max time to wait for the Snapshot Manifest packet to arrive from a peer after it's being asked.
222
225
const SNAPSHOT_MANIFEST_TIMEOUT : Duration = Duration :: from_secs ( 5 ) ;
@@ -318,6 +321,7 @@ pub enum PeerAsking {
318
321
BlockHeaders ,
319
322
BlockBodies ,
320
323
BlockReceipts ,
324
+ PooledTransactions ,
321
325
SnapshotManifest ,
322
326
SnapshotData ,
323
327
PrivateState ,
@@ -352,6 +356,8 @@ pub struct PeerInfo {
352
356
network_id : u64 ,
353
357
/// Peer best block hash
354
358
latest_hash : H256 ,
359
+ /// Unpropagated tx pool hashes
360
+ unsent_pooled_hashes : Option < H256FastSet > ,
355
361
/// Peer total difficulty if known
356
362
difficulty : Option < U256 > ,
357
363
/// Type of data currently being requested by us from a peer.
@@ -360,6 +366,8 @@ pub struct PeerInfo {
360
366
asking_blocks : Vec < H256 > ,
361
367
/// Holds requested header hash if currently requesting block header by hash
362
368
asking_hash : Option < H256 > ,
369
+ /// Holds requested transaction IDs
370
+ asking_pooled_transactions : Option < Vec < H256 > > ,
363
371
/// Holds requested private state hash
364
372
asking_private_state : Option < H256 > ,
365
373
/// Holds requested snapshot chunk hash if any.
@@ -669,6 +677,13 @@ enum PeerState {
669
677
SameBlock
670
678
}
671
679
680
+ #[ derive( Clone , MallocSizeOf ) ]
681
+ struct UnfetchedTransaction {
682
+ announcer : PeerId ,
683
+ next_fetch : Instant ,
684
+ tries : usize ,
685
+ }
686
+
672
687
/// Blockchain sync handler.
673
688
/// See module documentation for more details.
674
689
#[ derive( MallocSizeOf ) ]
@@ -708,6 +723,8 @@ pub struct ChainSync {
708
723
sync_start_time : Option < Instant > ,
709
724
/// Transactions propagation statistics
710
725
transactions_stats : TransactionsStats ,
726
+ /// Transactions whose hash has been announced, but that we have not fetched
727
+ unfetched_pooled_transactions : H256FastMap < UnfetchedTransaction > ,
711
728
/// Enable ancient block downloading
712
729
download_old_blocks : bool ,
713
730
/// Shared private tx service.
@@ -751,6 +768,7 @@ impl ChainSync {
751
768
snapshot : Snapshot :: new ( ) ,
752
769
sync_start_time : None ,
753
770
transactions_stats : TransactionsStats :: default ( ) ,
771
+ unfetched_pooled_transactions : Default :: default ( ) ,
754
772
private_tx_handler,
755
773
warp_sync : config. warp_sync ,
756
774
status_sinks : Vec :: new ( )
@@ -764,7 +782,7 @@ impl ChainSync {
764
782
let last_imported_number = self . new_blocks . last_imported_block_number ( ) ;
765
783
SyncStatus {
766
784
state : self . state . clone ( ) ,
767
- protocol_version : ETH_PROTOCOL_VERSION_64 . 0 ,
785
+ protocol_version : ETH_PROTOCOL_VERSION_65 . 0 ,
768
786
network_id : self . network_id ,
769
787
start_block_number : self . starting_block ,
770
788
last_imported_block_number : Some ( last_imported_number) ,
@@ -798,8 +816,17 @@ impl ChainSync {
798
816
799
817
/// Updates the set of transactions recently sent to this peer to avoid spamming.
800
818
pub fn transactions_received ( & mut self , txs : & [ UnverifiedTransaction ] , peer_id : PeerId ) {
801
- if let Some ( peer_info) = self . peers . get_mut ( & peer_id) {
802
- peer_info. last_sent_transactions . extend ( txs. iter ( ) . map ( |tx| tx. hash ( ) ) ) ;
819
+ for ( id, peer) in & mut self . peers {
820
+ let hashes = txs. iter ( ) . map ( |tx| tx. hash ( ) ) ;
821
+ if * id == peer_id {
822
+ peer. last_sent_transactions . extend ( hashes) ;
823
+ } else if let Some ( s) = & mut peer. unsent_pooled_hashes {
824
+ s. extend ( hashes) ;
825
+ }
826
+ }
827
+
828
+ for tx in txs {
829
+ self . unfetched_pooled_transactions . remove ( & tx. hash ( ) ) ;
803
830
}
804
831
}
805
832
@@ -1149,6 +1176,48 @@ impl ChainSync {
1149
1176
}
1150
1177
}
1151
1178
1179
+ // get the peer to give us at least some of announced but unfetched transactions
1180
+ if !self . unfetched_pooled_transactions . is_empty ( ) {
1181
+ if let Some ( s) = & mut self . peers . get_mut ( & peer_id) . expect ( "this is always an active peer; qed" ) . asking_pooled_transactions {
1182
+ let now = Instant :: now ( ) ;
1183
+
1184
+ let mut new_asking_pooled_transactions = s. iter ( ) . copied ( ) . collect :: < HashSet < _ > > ( ) ;
1185
+ let mut remaining_unfetched_pooled_transactions = self . unfetched_pooled_transactions . clone ( ) ;
1186
+ for ( hash, mut item) in self . unfetched_pooled_transactions . drain ( ) {
1187
+ if new_asking_pooled_transactions. len ( ) >= 256 {
1188
+ // can't request any more transactions
1189
+ break ;
1190
+ }
1191
+
1192
+ // if enough time has passed since last attempt...
1193
+ if item. next_fetch < now {
1194
+ // ...queue this hash for requesting
1195
+ new_asking_pooled_transactions. insert ( hash) ;
1196
+ item. tries += 1 ;
1197
+
1198
+ // if we just started asking for it, queue it to be asked later on again
1199
+ if item. tries < 5 {
1200
+ item. next_fetch = now + ( POOLED_TRANSACTIONS_TIMEOUT / 2 ) ;
1201
+ remaining_unfetched_pooled_transactions. insert ( hash, item) ;
1202
+ } else {
1203
+ // ...otherwise we assume this transaction does not exist and remove its hash from request queue
1204
+ remaining_unfetched_pooled_transactions. remove ( & hash) ;
1205
+ }
1206
+ }
1207
+ }
1208
+
1209
+ let new_asking_pooled_transactions = new_asking_pooled_transactions. into_iter ( ) . collect :: < Vec < _ > > ( ) ;
1210
+ SyncRequester :: request_pooled_transactions ( self , io, peer_id, & new_asking_pooled_transactions) ;
1211
+
1212
+ self . peers . get_mut ( & peer_id) . expect ( "this is always an active peer; qed" ) . asking_pooled_transactions = Some ( new_asking_pooled_transactions) ;
1213
+ self . unfetched_pooled_transactions = remaining_unfetched_pooled_transactions;
1214
+
1215
+ return ;
1216
+ } else {
1217
+ trace ! ( target: "sync" , "Skipping transaction fetch for peer {} as they don't support eth/65" , peer_id) ;
1218
+ }
1219
+ }
1220
+
1152
1221
// Only ask for old blocks if the peer has an equal or higher difficulty
1153
1222
let equal_or_higher_difficulty = peer_difficulty. map_or ( true , |pd| pd >= syncing_difficulty) ;
1154
1223
@@ -1340,6 +1409,7 @@ impl ChainSync {
1340
1409
PeerAsking :: BlockHeaders => elapsed > HEADERS_TIMEOUT ,
1341
1410
PeerAsking :: BlockBodies => elapsed > BODIES_TIMEOUT ,
1342
1411
PeerAsking :: BlockReceipts => elapsed > RECEIPTS_TIMEOUT ,
1412
+ PeerAsking :: PooledTransactions => elapsed > POOLED_TRANSACTIONS_TIMEOUT ,
1343
1413
PeerAsking :: Nothing => false ,
1344
1414
PeerAsking :: ForkHeader => elapsed > FORK_HEADER_TIMEOUT ,
1345
1415
PeerAsking :: SnapshotManifest => elapsed > SNAPSHOT_MANIFEST_TIMEOUT ,
@@ -1668,10 +1738,12 @@ pub mod tests {
1668
1738
genesis : H256 :: zero ( ) ,
1669
1739
network_id : 0 ,
1670
1740
latest_hash : peer_latest_hash,
1741
+ unsent_pooled_hashes : Some ( Default :: default ( ) ) ,
1671
1742
difficulty : None ,
1672
1743
asking : PeerAsking :: Nothing ,
1673
1744
asking_blocks : Vec :: new ( ) ,
1674
1745
asking_hash : None ,
1746
+ asking_pooled_transactions : Some ( Vec :: new ( ) ) ,
1675
1747
asking_private_state : None ,
1676
1748
ask_time : Instant :: now ( ) ,
1677
1749
last_sent_transactions : Default :: default ( ) ,
0 commit comments