Return fetch error on response that fails verification or validation (#6864)

This commit is contained in:
Emilia Hane
2024-03-03 15:31:28 +01:00
committed by GitHub
parent 7fa7726074
commit ba1061a043
2 changed files with 57 additions and 6 deletions

View File

@@ -843,6 +843,23 @@ impl TransactionFetcher {
match result {
Ok(Ok(transactions)) => {
//
// 1. peer has failed to serve any of the hashes it has announced to us that we,
// as a follow, have requested
//
if transactions.is_empty() {
trace!(target: "net::tx",
peer_id=format!("{peer_id:#}"),
requested_hashes_len=requested_hashes.len(),
"received empty `PooledTransactions` response from peer, peer failed to serve hashes it announced"
);
return FetchEvent::EmptyResponse { peer_id }
}
//
// 2. filter out hashes that we didn't request
//
let payload = UnverifiedPooledTransactions::new(transactions);
let unverified_len = payload.len();
@@ -858,25 +875,40 @@ impl TransactionFetcher {
"received `PooledTransactions` response from peer with entries that didn't verify against request, filtered out transactions"
);
}
// peer has only sent hashes that we didn't request
if verified_payload.is_empty() {
return FetchEvent::FetchError { peer_id, error: RequestError::BadResponse }
}
//
// 3. stateless validation of payload, e.g. dedup
//
let unvalidated_payload_len = verified_payload.len();
// todo: report peer for sending invalid response
// <https://github.com/paradigmxyz/reth/issues/6529>
let (validation_outcome, valid_payload) =
self.filter_valid_message.partially_filter_valid_entries(verified_payload);
// todo: validate based on announced tx size/type and report peer for sending
// invalid response <https://github.com/paradigmxyz/reth/issues/6529>. requires
// passing the rlp encoded length down from active session along with the decoded
// tx.
if let FilterOutcome::ReportPeer = validation_outcome {
trace!(target: "net::tx",
peer_id=format!("{peer_id:#}"),
unvalidated_payload_len=unvalidated_payload_len,
valid_payload_len=valid_payload.len(),
"received invalid `PooledTransactions` response from peer, filtered out invalid entries"
"received invalid `PooledTransactions` response from peer, filtered out duplicate entries"
);
}
// valid payload will have at least one transaction at this point. even if the tx
// size/type announced by the peer is different to the actual tx size/type, pass on
// to pending pool imports pipeline for validation.
// clear received hashes
//
// 4. clear received hashes
//
let requested_hashes_len = requested_hashes.len();
let mut fetched = Vec::with_capacity(valid_payload.len());
requested_hashes.retain(|requested_hash| {
if valid_payload.contains_key(requested_hash) {
@@ -888,7 +920,18 @@ impl TransactionFetcher {
});
fetched.shrink_to_fit();
// buffer left over hashes
if fetched.len() < requested_hashes_len {
trace!(target: "net::tx",
peer_id=format!("{peer_id:#}"),
requested_hashes_len=requested_hashes_len,
fetched_len=fetched.len(),
"peer failed to serve hashes it announced"
);
}
//
// 5. buffer left over hashes
//
self.try_buffer_hashes_for_retry(requested_hashes, &peer_id);
let transactions =
@@ -994,6 +1037,11 @@ pub enum FetchEvent {
/// The specific error that occurred while fetching.
error: RequestError,
},
/// An empty response was received.
EmptyResponse {
/// The ID of the sender.
peer_id: PeerId,
},
}
/// An inflight request for [`PooledTransactions`] from a peer.

View File

@@ -1280,6 +1280,9 @@ where
trace!(target: "net::tx", ?peer_id, %error, "requesting transactions from peer failed");
this.on_request_error(peer_id, error);
}
FetchEvent::EmptyResponse { peer_id } => {
trace!(target: "net::tx", ?peer_id, "peer returned empty response");
}
}
some_ready = true;
}