mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
add test_discard_blobs_on_blob_tx_eviction unit test (#6445)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -6970,6 +6970,7 @@ dependencies = [
|
||||
"revm",
|
||||
"schnellru",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
|
||||
@ -23,8 +23,11 @@ pub use legacy::TxLegacy;
|
||||
pub use meta::TransactionMeta;
|
||||
#[cfg(feature = "c-kzg")]
|
||||
pub use pooled::{PooledTransactionsElement, PooledTransactionsElementEcRecovered};
|
||||
#[cfg(all(feature = "c-kzg", feature = "arbitrary"))]
|
||||
pub use sidecar::generate_blob_sidecar;
|
||||
#[cfg(feature = "c-kzg")]
|
||||
pub use sidecar::{BlobTransaction, BlobTransactionSidecar, BlobTransactionValidationError};
|
||||
|
||||
pub use signature::Signature;
|
||||
pub use tx_type::{
|
||||
TxType, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, LEGACY_TX_TYPE_ID,
|
||||
|
||||
@ -482,8 +482,9 @@ impl proptest::arbitrary::Arbitrary for BlobTransactionSidecar {
|
||||
type Strategy = BoxedStrategy<BlobTransactionSidecar>;
|
||||
}
|
||||
|
||||
/// Generates a [`BlobTransactionSidecar`] structure containing blobs, commitments, and proofs.
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
fn generate_blob_sidecar(blobs: Vec<Blob>) -> BlobTransactionSidecar {
|
||||
pub fn generate_blob_sidecar(blobs: Vec<Blob>) -> BlobTransactionSidecar {
|
||||
let kzg_settings = MAINNET_KZG_TRUSTED_SETUP.clone();
|
||||
|
||||
let commitments: Vec<Bytes48> = blobs
|
||||
|
||||
@ -64,6 +64,7 @@ proptest.workspace = true
|
||||
criterion.workspace = true
|
||||
assert_matches.workspace = true
|
||||
tempfile.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["serde"]
|
||||
|
||||
@ -4,7 +4,7 @@ use reth_primitives::B256;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
/// An in-memory blob store.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct InMemoryBlobStore {
|
||||
inner: Arc<InMemoryBlobStoreInner>,
|
||||
}
|
||||
@ -16,6 +16,12 @@ struct InMemoryBlobStoreInner {
|
||||
size_tracker: BlobStoreSize,
|
||||
}
|
||||
|
||||
impl PartialEq for InMemoryBlobStoreInner {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.store.read().eq(&other.store.read())
|
||||
}
|
||||
}
|
||||
|
||||
impl BlobStore for InMemoryBlobStore {
|
||||
fn insert(&self, tx: B256, data: BlobTransactionSidecar) -> Result<(), BlobStoreError> {
|
||||
let mut store = self.inner.store.write();
|
||||
|
||||
@ -4,7 +4,10 @@ pub use disk::{DiskFileBlobStore, DiskFileBlobStoreConfig, OpenDiskFileBlobStore
|
||||
pub use mem::InMemoryBlobStore;
|
||||
pub use noop::NoopBlobStore;
|
||||
use reth_primitives::{BlobTransactionSidecar, B256};
|
||||
use std::{fmt, sync::atomic::AtomicUsize};
|
||||
use std::{
|
||||
fmt,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
pub use tracker::{BlobStoreCanonTracker, BlobStoreUpdates};
|
||||
|
||||
pub mod disk;
|
||||
@ -85,32 +88,39 @@ pub(crate) struct BlobStoreSize {
|
||||
impl BlobStoreSize {
|
||||
#[inline]
|
||||
pub(crate) fn add_size(&self, add: usize) {
|
||||
self.data_size.fetch_add(add, std::sync::atomic::Ordering::Relaxed);
|
||||
self.data_size.fetch_add(add, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn sub_size(&self, sub: usize) {
|
||||
self.data_size.fetch_sub(sub, std::sync::atomic::Ordering::Relaxed);
|
||||
self.data_size.fetch_sub(sub, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn update_len(&self, len: usize) {
|
||||
self.num_blobs.store(len, std::sync::atomic::Ordering::Relaxed);
|
||||
self.num_blobs.store(len, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn inc_len(&self, add: usize) {
|
||||
self.num_blobs.fetch_add(add, std::sync::atomic::Ordering::Relaxed);
|
||||
self.num_blobs.fetch_add(add, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn data_size(&self) -> usize {
|
||||
self.data_size.load(std::sync::atomic::Ordering::Relaxed)
|
||||
self.data_size.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn blobs_len(&self) -> usize {
|
||||
self.num_blobs.load(std::sync::atomic::Ordering::Relaxed)
|
||||
self.num_blobs.load(Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BlobStoreSize {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.data_size.load(Ordering::Relaxed) == other.data_size.load(Ordering::Relaxed) &&
|
||||
self.num_blobs.load(Ordering::Relaxed) == other.num_blobs.load(Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1103,3 +1103,95 @@ impl<T: PoolTransaction> OnNewCanonicalStateOutcome<T> {
|
||||
FullPendingTransactionIter { kind, iter }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
blobstore::{BlobStore, InMemoryBlobStore},
|
||||
test_utils::{MockTransaction, TestPoolBuilder},
|
||||
validate::ValidTransaction,
|
||||
BlockInfo, PoolConfig, SubPoolLimit, TransactionOrigin, TransactionValidationOutcome, U256,
|
||||
};
|
||||
use reth_primitives::{kzg::Blob, transaction::generate_blob_sidecar};
|
||||
use std::{fs, path::PathBuf};
|
||||
|
||||
#[test]
|
||||
fn test_discard_blobs_on_blob_tx_eviction() {
|
||||
// Define the maximum limit for blobs in the sub-pool.
|
||||
let blob_limit = SubPoolLimit::new(1000, usize::MAX);
|
||||
|
||||
// Create a test pool with default configuration and the specified blob limit.
|
||||
let test_pool = &TestPoolBuilder::default()
|
||||
.with_config(PoolConfig { blob_limit, ..Default::default() })
|
||||
.pool;
|
||||
|
||||
// Set the block info for the pool, including a pending blob fee.
|
||||
test_pool
|
||||
.set_block_info(BlockInfo { pending_blob_fee: Some(10_000_000), ..Default::default() });
|
||||
|
||||
// Read the contents of the JSON file into a string.
|
||||
let json_content = fs::read_to_string(
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test_data/blob1.json"),
|
||||
)
|
||||
.expect("Failed to read the blob data file");
|
||||
|
||||
// Parse the JSON contents into a serde_json::Value.
|
||||
let json_value: serde_json::Value =
|
||||
serde_json::from_str(&json_content).expect("Failed to deserialize JSON");
|
||||
|
||||
// Extract blob data from JSON and convert it to Blob.
|
||||
let blobs: Vec<Blob> = vec![Blob::from_hex(
|
||||
// Extract the "data" field from the JSON and parse it as a string.
|
||||
json_value.get("data").unwrap().as_str().expect("Data is not a valid string"),
|
||||
)
|
||||
.unwrap()];
|
||||
|
||||
// Generate a BlobTransactionSidecar from the blobs.
|
||||
let sidecar = generate_blob_sidecar(blobs.clone());
|
||||
|
||||
// Create an in-memory blob store.
|
||||
let blob_store = InMemoryBlobStore::default();
|
||||
|
||||
// Loop to add transactions to the pool and test blob eviction.
|
||||
for n in 0..blob_limit.max_txs + 10 {
|
||||
// Create a mock transaction with the generated blob sidecar.
|
||||
let mut tx = MockTransaction::eip4844_with_sidecar(sidecar.clone());
|
||||
|
||||
// Set non zero size
|
||||
tx.set_size(1844674407370951);
|
||||
|
||||
// Insert the sidecar into the blob store if the current index is within the blob limit.
|
||||
if n < blob_limit.max_txs {
|
||||
blob_store.insert(tx.get_hash(), sidecar.clone()).unwrap();
|
||||
}
|
||||
|
||||
// Add the transaction to the pool with external origin and valid outcome.
|
||||
test_pool
|
||||
.add_transaction(
|
||||
TransactionOrigin::External,
|
||||
TransactionValidationOutcome::Valid {
|
||||
balance: U256::from(1_000),
|
||||
state_nonce: 0,
|
||||
transaction: ValidTransaction::ValidWithSidecar {
|
||||
transaction: tx,
|
||||
sidecar: sidecar.clone(),
|
||||
},
|
||||
propagate: true,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Evict the worst transactions from the pool.
|
||||
test_pool.discard_worst();
|
||||
}
|
||||
|
||||
// Assert that the size of the pool's blob component is equal to the maximum blob limit.
|
||||
assert_eq!(test_pool.size().blob, blob_limit.max_txs);
|
||||
|
||||
// Assert that the size of the pool's blob_size component matches the expected value.
|
||||
assert_eq!(test_pool.size().blob_size, 1844674407370951000);
|
||||
|
||||
// Assert that the pool's blob store matches the expected blob store.
|
||||
assert_eq!(*test_pool.blob_store(), blob_store);
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,6 +310,16 @@ impl MockTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new EIP4844 transaction with a provided sidecar
|
||||
pub fn eip4844_with_sidecar(sidecar: BlobTransactionSidecar) -> Self {
|
||||
let mut transaction = Self::eip4844();
|
||||
if let MockTransaction::Eip4844 { sidecar: ref mut existing_sidecar, .. } = &mut transaction
|
||||
{
|
||||
*existing_sidecar = sidecar;
|
||||
}
|
||||
transaction
|
||||
}
|
||||
|
||||
/// Returns a new EIP2930 transaction with random address and hash and empty values
|
||||
pub fn eip2930() -> Self {
|
||||
MockTransaction::Eip2930 {
|
||||
|
||||
@ -1140,7 +1140,7 @@ impl PoolSize {
|
||||
}
|
||||
|
||||
/// Represents the current status of the pool.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct BlockInfo {
|
||||
/// Hash for the currently tracked block.
|
||||
pub last_seen_block_hash: B256,
|
||||
|
||||
4
crates/transaction-pool/test_data/blob1.json
Normal file
4
crates/transaction-pool/test_data/blob1.json
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user