mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
Bug fix, increase tx response soft limit (#6301)
This commit is contained in:
@ -16,7 +16,7 @@ use std::{
|
||||
use tokio::sync::{mpsc::error::TrySendError, oneshot, oneshot::error::RecvError};
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use super::{Peer, PooledTransactions, FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT};
|
||||
use super::{Peer, PooledTransactions, POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE};
|
||||
|
||||
/// Maximum concurrent [`GetPooledTxRequest`]s to allow per peer.
|
||||
pub(super) const MAX_CONCURRENT_TX_REQUESTS_PER_PEER: u8 = 1;
|
||||
@ -175,7 +175,7 @@ impl TransactionFetcher {
|
||||
if let Some(size) = self.eth68_meta.peek(&hash) {
|
||||
let next_acc_size = *acc_size_response + size;
|
||||
|
||||
if next_acc_size <= FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT {
|
||||
if next_acc_size <= POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE {
|
||||
// only update accumulated size of tx response if tx will fit in without exceeding
|
||||
// soft limit
|
||||
*acc_size_response = next_acc_size;
|
||||
@ -205,7 +205,7 @@ impl TransactionFetcher {
|
||||
) -> Vec<TxHash> {
|
||||
if let Some(hash) = hashes.first() {
|
||||
if let Some(size) = self.eth68_meta.get(hash) {
|
||||
if *size >= FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT {
|
||||
if *size >= POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE {
|
||||
return hashes.split_off(1)
|
||||
}
|
||||
}
|
||||
@ -222,7 +222,8 @@ impl TransactionFetcher {
|
||||
hash=%hash,
|
||||
size=self.eth68_meta.peek(&hash).expect("should find size in `eth68-meta`"),
|
||||
acc_size_response=acc_size_response,
|
||||
FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT=FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT,
|
||||
POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE=
|
||||
POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE,
|
||||
"no space for hash in `GetPooledTransactions` request to peer"
|
||||
);
|
||||
|
||||
@ -233,7 +234,7 @@ impl TransactionFetcher {
|
||||
|
||||
// all hashes included in request and there is still space
|
||||
// todo: compare free space with min tx size
|
||||
if acc_size_response < FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT {
|
||||
if acc_size_response < POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE {
|
||||
self.fill_eth68_request_for_peer(hashes, peer_id, &mut acc_size_response);
|
||||
}
|
||||
|
||||
@ -498,7 +499,7 @@ impl TransactionFetcher {
|
||||
peer_id: PeerId,
|
||||
acc_size_response: &mut usize,
|
||||
) {
|
||||
if *acc_size_response >= FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT {
|
||||
if *acc_size_response >= POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE {
|
||||
return
|
||||
}
|
||||
|
||||
@ -522,11 +523,12 @@ impl TransactionFetcher {
|
||||
let mut next_acc_size = *acc_size_response;
|
||||
|
||||
// 1. Check acc size against limit, if so stop looping.
|
||||
if next_acc_size >= FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT {
|
||||
if next_acc_size >= POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE {
|
||||
trace!(target: "net::tx",
|
||||
peer_id=format!("{peer_id:#}"),
|
||||
acc_size_eth68_response=acc_size_response, // no change acc size
|
||||
FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT=FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT,
|
||||
POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE=
|
||||
POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE,
|
||||
"request to peer full"
|
||||
);
|
||||
|
||||
@ -568,7 +570,8 @@ impl TransactionFetcher {
|
||||
peer_id=format!("{peer_id:#}"),
|
||||
hash=%hash,
|
||||
acc_size_eth68_response=acc_size_response,
|
||||
FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT=FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT,
|
||||
POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE=
|
||||
POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE,
|
||||
"found buffered hash for request to peer"
|
||||
);
|
||||
}
|
||||
@ -629,7 +632,8 @@ impl TransactionFetcher {
|
||||
trace!(target: "net::tx",
|
||||
peer_id=format!("{peer_id:#}"),
|
||||
hash=%hash,
|
||||
FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT=FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT,
|
||||
POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE=
|
||||
POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE,
|
||||
"found buffered hash for request to peer"
|
||||
);
|
||||
}
|
||||
@ -818,12 +822,14 @@ mod test {
|
||||
B256::from_slice(&[6; 32]),
|
||||
];
|
||||
let eth68_hashes_sizes = [
|
||||
FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT - 4,
|
||||
FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT, // this one will not fit
|
||||
2, // this one will fit
|
||||
3, // but now this one won't
|
||||
2, /* this one will, no more txns will fit
|
||||
* after this */
|
||||
POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE - 4,
|
||||
POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE, // this one will not fit
|
||||
2, // this one will fit
|
||||
3, // but now this one won't
|
||||
2, /* this one will, no more txns
|
||||
* will
|
||||
* fit
|
||||
* after this */
|
||||
1,
|
||||
];
|
||||
|
||||
|
||||
@ -74,12 +74,11 @@ const PEER_TRANSACTION_CACHE_LIMIT: usize = 1024 * 10;
|
||||
/// Soft limit for NewPooledTransactions
|
||||
const NEW_POOLED_TRANSACTION_HASHES_SOFT_LIMIT: usize = 4096;
|
||||
|
||||
/// Soft limit for the message of full transactions in bytes.
|
||||
const FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT: usize = 100 * 1024;
|
||||
|
||||
/// Softlimit for the response size of a GetPooledTransactions message (2MB)
|
||||
const GET_POOLED_TRANSACTION_SOFT_LIMIT_SIZE: GetPooledTransactionLimit =
|
||||
GetPooledTransactionLimit::SizeSoftLimit(2 * 1024 * 1024);
|
||||
/// Soft limit for the response size of a GetPooledTransactions message (2MB) in bytes. Standard
|
||||
/// maximum response size. See specs
|
||||
///
|
||||
/// <https://github.com/ethereum/devp2p/blob/master/caps/eth.md#protocol-messages>.
|
||||
const POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE: usize = 2 * 1024 * 1024;
|
||||
|
||||
/// The future for inserting a function into the pool
|
||||
pub type PoolImportFuture = Pin<Box<dyn Future<Output = PoolResult<TxHash>> + Send + 'static>>;
|
||||
@ -300,9 +299,12 @@ where
|
||||
let _ = response.send(Ok(PooledTransactions::default()));
|
||||
return
|
||||
}
|
||||
let transactions = self
|
||||
.pool
|
||||
.get_pooled_transaction_elements(request.0, GET_POOLED_TRANSACTION_SOFT_LIMIT_SIZE);
|
||||
let transactions = self.pool.get_pooled_transaction_elements(
|
||||
request.0,
|
||||
GetPooledTransactionLimit::ResponseSizeSoftLimit(
|
||||
POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE,
|
||||
),
|
||||
);
|
||||
|
||||
// we sent a response at which point we assume that the peer is aware of the
|
||||
// transactions
|
||||
@ -1112,7 +1114,7 @@ impl PropagateTransaction {
|
||||
}
|
||||
|
||||
/// Helper type for constructing the full transaction message that enforces the
|
||||
/// `FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT`
|
||||
/// [`POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE`].
|
||||
#[derive(Default)]
|
||||
struct FullTransactionsBuilder {
|
||||
total_size: usize,
|
||||
@ -1125,7 +1127,7 @@ impl FullTransactionsBuilder {
|
||||
/// Append a transaction to the list if it doesn't exceed the maximum target size.
|
||||
fn push(&mut self, transaction: &PropagateTransaction) {
|
||||
let new_size = self.total_size + transaction.size;
|
||||
if new_size > FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT {
|
||||
if new_size > POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE {
|
||||
return
|
||||
}
|
||||
|
||||
@ -1790,8 +1792,8 @@ mod tests {
|
||||
let eth_version = EthVersion::Eth68;
|
||||
let unseen_eth68_hashes = [B256::from_slice(&[1; 32]), B256::from_slice(&[2; 32])];
|
||||
let unseen_eth68_hashes_sizes = [
|
||||
FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT / 2,
|
||||
FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT / 2 - 4,
|
||||
POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE / 2,
|
||||
POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE / 2 - 4,
|
||||
];
|
||||
// hashes and sizes to buffer in reverse order so that seen_eth68_hashes[0] and
|
||||
// seen_eth68_hashes_sizes[0] are lru
|
||||
@ -1828,7 +1830,9 @@ mod tests {
|
||||
let mut backups = default_cache();
|
||||
backups.insert(peer_id_other);
|
||||
tx_fetcher.unknown_hashes.insert(hash_other, (0, backups));
|
||||
tx_fetcher.eth68_meta.insert(hash_other, FULL_TRANSACTIONS_PACKET_SIZE_SOFT_LIMIT - 2); // a big tx
|
||||
tx_fetcher
|
||||
.eth68_meta
|
||||
.insert(hash_other, POOLED_TRANSACTIONS_RESPONSE_SOFT_LIMIT_BYTE_SIZE - 2); // a big tx
|
||||
tx_fetcher.buffered_hashes.insert(hash_other);
|
||||
|
||||
let (peer, mut to_mock_session_rx) = new_mock_session(peer_id, eth_version);
|
||||
|
||||
@ -1134,7 +1134,7 @@ pub enum GetPooledTransactionLimit {
|
||||
/// No limit, return all transactions.
|
||||
None,
|
||||
/// Enforce a size limit on the returned transactions, for example 2MB
|
||||
SizeSoftLimit(usize),
|
||||
ResponseSizeSoftLimit(usize),
|
||||
}
|
||||
|
||||
impl GetPooledTransactionLimit {
|
||||
@ -1143,7 +1143,7 @@ impl GetPooledTransactionLimit {
|
||||
pub fn exceeds(&self, size: usize) -> bool {
|
||||
match self {
|
||||
GetPooledTransactionLimit::None => false,
|
||||
GetPooledTransactionLimit::SizeSoftLimit(limit) => size > *limit,
|
||||
GetPooledTransactionLimit::ResponseSizeSoftLimit(limit) => size > *limit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/// TX_SLOT_SIZE is used to calculate how many data slots a single transaction
|
||||
/// takes up based on its size. The slots are used as DoS protection, ensuring
|
||||
/// [`TX_SLOT_BYTE_SIZE`] is used to calculate how many data slots a single transaction
|
||||
/// takes up based on its byte size. The slots are used as DoS protection, ensuring
|
||||
/// that validating a new transaction remains a constant operation (in reality
|
||||
/// O(maxslots), where max slots are 4 currently).
|
||||
pub const TX_SLOT_SIZE: usize = 32 * 1024;
|
||||
pub const TX_SLOT_BYTE_SIZE: usize = 32 * 1024;
|
||||
|
||||
/// TX_MAX_SIZE is the maximum size a single transaction can have. This field has
|
||||
/// [`MAX_TX_INPUT_BYTES`] is the maximum size a single transaction can have. This field has
|
||||
/// non-trivial consequences: larger transactions are significantly harder and
|
||||
/// more expensive to propagate; larger transactions also take more resources
|
||||
/// to validate whether they fit into the pool or not.
|
||||
pub const TX_MAX_SIZE: usize = 4 * TX_SLOT_SIZE; // 128KB
|
||||
pub const MAX_TX_INPUT_BYTES: usize = 4 * TX_SLOT_BYTE_SIZE; // 128KB
|
||||
|
||||
/// Maximum bytecode to permit for a contract
|
||||
pub const MAX_CODE_SIZE: usize = 24576;
|
||||
/// Maximum bytecode to permit for a contract.
|
||||
pub const MAX_CODE_BYTE_SIZE: usize = 24576;
|
||||
|
||||
/// Maximum initcode to permit in a creation transaction and create instructions
|
||||
pub const MAX_INIT_CODE_SIZE: usize = 2 * MAX_CODE_SIZE;
|
||||
/// Maximum initcode to permit in a creation transaction and create instructions.
|
||||
pub const MAX_INIT_CODE_BYTE_SIZE: usize = 2 * MAX_CODE_BYTE_SIZE;
|
||||
|
||||
@ -4,7 +4,7 @@ use crate::{
|
||||
blobstore::BlobStore,
|
||||
error::{Eip4844PoolTransactionError, InvalidPoolTransactionError},
|
||||
traits::TransactionOrigin,
|
||||
validate::{ValidTransaction, ValidationTask, MAX_INIT_CODE_SIZE, TX_MAX_SIZE},
|
||||
validate::{ValidTransaction, ValidationTask, MAX_INIT_CODE_BYTE_SIZE, MAX_TX_INPUT_BYTES},
|
||||
EthBlobTransactionSidecar, EthPoolTransaction, LocalTransactionConfig, PoolTransaction,
|
||||
TransactionValidationOutcome, TransactionValidationTaskExecutor, TransactionValidator,
|
||||
};
|
||||
@ -201,17 +201,17 @@ where
|
||||
};
|
||||
|
||||
// Reject transactions over defined size to prevent DOS attacks
|
||||
if transaction.size() > TX_MAX_SIZE {
|
||||
if transaction.size() > MAX_TX_INPUT_BYTES {
|
||||
let size = transaction.size();
|
||||
return TransactionValidationOutcome::Invalid(
|
||||
transaction,
|
||||
InvalidPoolTransactionError::OversizedData(size, TX_MAX_SIZE),
|
||||
InvalidPoolTransactionError::OversizedData(size, MAX_TX_INPUT_BYTES),
|
||||
)
|
||||
}
|
||||
|
||||
// Check whether the init code size has been exceeded.
|
||||
if self.fork_tracker.is_shanghai_activated() {
|
||||
if let Err(err) = ensure_max_init_code_size(&transaction, MAX_INIT_CODE_SIZE) {
|
||||
if let Err(err) = ensure_max_init_code_size(&transaction, MAX_INIT_CODE_BYTE_SIZE) {
|
||||
return TransactionValidationOutcome::Invalid(transaction, err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,9 @@ pub use eth::*;
|
||||
pub use task::{TransactionValidationTaskExecutor, ValidationTask};
|
||||
|
||||
/// Validation constants.
|
||||
pub use constants::{MAX_CODE_SIZE, MAX_INIT_CODE_SIZE, TX_MAX_SIZE, TX_SLOT_SIZE};
|
||||
pub use constants::{
|
||||
MAX_CODE_BYTE_SIZE, MAX_INIT_CODE_BYTE_SIZE, MAX_TX_INPUT_BYTES, TX_SLOT_BYTE_SIZE,
|
||||
};
|
||||
|
||||
/// A Result type returned after checking a transaction's validity.
|
||||
#[derive(Debug)]
|
||||
|
||||
Reference in New Issue
Block a user