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:
Thomas Coratger
2024-02-08 14:15:09 +01:00
committed by GitHub
parent 48bd37e7a6
commit 672246d400
10 changed files with 138 additions and 10 deletions

1
Cargo.lock generated
View File

@ -6970,6 +6970,7 @@ dependencies = [
"revm",
"schnellru",
"serde",
"serde_json",
"tempfile",
"thiserror",
"tokio",

View File

@ -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,

View File

@ -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

View File

@ -64,6 +64,7 @@ proptest.workspace = true
criterion.workspace = true
assert_matches.workspace = true
tempfile.workspace = true
serde_json.workspace = true
[features]
default = ["serde"]

View File

@ -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();

View File

@ -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)
}
}

View File

@ -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);
}
}

View File

@ -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 {

View File

@ -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,

File diff suppressed because one or more lines are too long