Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fees transactions order in mempool #492

Merged
merged 10 commits into from
Dec 17, 2024
9 changes: 7 additions & 2 deletions crates/cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use anvil_zksync_config::types::{
AccountGenerator, CacheConfig, CacheType, Genesis, LogLevel, ShowCalls, ShowGasDetails,
ShowStorageLogs, ShowVMDetails, SystemContractsOptions,
};
use anvil_zksync_config::TestNodeConfig;
use anvil_zksync_config::{types::TransactionOrder, TestNodeConfig};
use clap::{arg, command, Parser, Subcommand};
use rand::{rngs::StdRng, SeedableRng};
use std::env;
Expand Down Expand Up @@ -251,6 +251,10 @@ pub struct Cli {
/// Disable CORS.
#[arg(long, default_missing_value = "true", num_args(0..=1), conflicts_with = "allow_origin", help_heading = "Server options")]
pub no_cors: Option<bool>,

/// How transactions are sorted in the mempool.
Romsters marked this conversation as resolved.
Show resolved Hide resolved
#[arg(long, default_value = "fifo")]
pub order: TransactionOrder,
}

#[derive(Debug, Subcommand, Clone)]
Expand Down Expand Up @@ -391,7 +395,8 @@ impl Cli {
.with_block_time(self.block_time)
.with_no_mining(self.no_mining)
.with_allow_origin(self.allow_origin)
.with_no_cors(self.no_cors);
.with_no_cors(self.no_cors)
.with_transactions_order(self.order);

if self.emulate_evm && self.dev_system_contracts != Some(SystemContractsOptions::Local) {
return Err(eyre::eyre!(
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ async fn main() -> anyhow::Result<()> {

let time = TimestampManager::default();
let impersonation = ImpersonationManager::default();
let pool = TxPool::new(impersonation.clone());
let pool = TxPool::new(impersonation.clone(), config.transactions_order);
Romsters marked this conversation as resolved.
Show resolved Hide resolved
let sealing_mode = if config.no_mining {
BlockSealerMode::noop()
} else if let Some(block_time) = config.block_time {
Expand Down
10 changes: 10 additions & 0 deletions crates/config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ pub struct TestNodeConfig {
pub allow_origin: String,
/// Disable CORS if true
pub no_cors: bool,
/// How transactions are sorted in the mempool
pub transactions_order: TransactionOrder,
Romsters marked this conversation as resolved.
Show resolved Hide resolved
}

impl Default for TestNodeConfig {
Expand Down Expand Up @@ -175,6 +177,7 @@ impl Default for TestNodeConfig {
no_mining: false,

max_transactions: 1000,
transactions_order: TransactionOrder::Fifo,
Romsters marked this conversation as resolved.
Show resolved Hide resolved

// Server configuration
allow_origin: "*".to_string(),
Expand Down Expand Up @@ -878,6 +881,13 @@ impl TestNodeConfig {
self
}

// Set transactions order in the mempool
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Set transactions order in the mempool
// Set transaction order in the mempool

#[must_use]
pub fn with_transactions_order(mut self, transactions_order: TransactionOrder) -> Self {
Romsters marked this conversation as resolved.
Show resolved Hide resolved
self.transactions_order = transactions_order;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.transactions_order = transactions_order;
self.transaction_order = transaction_order;

self
}

// Set allow_origin CORS header
#[must_use]
pub fn with_allow_origin(mut self, allow_origin: String) -> Self {
Expand Down
2 changes: 2 additions & 0 deletions crates/config/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod cache;
mod genesis;
mod log;
mod show_details;
mod transaction_order;

pub use account_generator::AccountGenerator;
pub use cache::{CacheConfig, CacheType};
Expand All @@ -11,6 +12,7 @@ pub use genesis::Genesis;
pub use log::LogLevel;
use serde::Deserialize;
pub use show_details::{ShowCalls, ShowGasDetails, ShowStorageLogs, ShowVMDetails};
pub use transaction_order::{TransactionOrder, TransactionPriority};

#[derive(Deserialize, Default, Debug, Copy, Clone, PartialEq, ValueEnum)]
pub enum SystemContractsOptions {
Expand Down
57 changes: 57 additions & 0 deletions crates/config/src/types/transaction_order.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use std::fmt;
use std::str::FromStr;
use zksync_types::{l2::L2Tx, U256};

/// Metric value for the priority of a transaction.
///
/// The `TransactionPriority` determines the ordering of two transactions.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct TransactionPriority(pub U256);

/// Modes that determine the transaction ordering of the mempool
///
/// This type controls the transaction order via the priority metric of a transaction
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum TransactionOrder {
/// Keep the pool transactions sorted in the order they arrive.
///
/// This will essentially assign every transaction the exact priority so the order is
/// determined by their internal submission number
Fifo,
/// This means that it prioritizes transactions based on the fees paid to the miner.
#[default]
Fees,
}

impl TransactionOrder {
/// Returns the priority of the transactions
pub fn priority(&self, tx: &L2Tx) -> TransactionPriority {
match self {
Self::Fifo => TransactionPriority::default(),
Self::Fees => TransactionPriority(tx.common_data.fee.max_fee_per_gas),
}
}
}

impl FromStr for TransactionOrder {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
let order = match s.as_str() {
"fees" => Self::Fees,
"fifo" => Self::Fifo,
_ => return Err(format!("Unknown TransactionOrder: `{s}`")),
};
Ok(order)
}
}

impl fmt::Display for TransactionOrder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
TransactionOrder::Fifo => f.write_str("fifo"),
TransactionOrder::Fees => f.write_str("fees"),
}
}
}
17 changes: 10 additions & 7 deletions crates/core/src/node/in_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use anvil_zksync_config::constants::{
};
use anvil_zksync_config::types::{
CacheConfig, Genesis, ShowCalls, ShowGasDetails, ShowStorageLogs, ShowVMDetails,
SystemContractsOptions,
SystemContractsOptions, TransactionOrder,
};
use anvil_zksync_config::TestNodeConfig;
use colored::Colorize;
Expand Down Expand Up @@ -1108,7 +1108,7 @@ fn contract_address_from_tx_result(execution_result: &VmExecutionResultAndLogs)
impl Default for InMemoryNode {
fn default() -> Self {
let impersonation = ImpersonationManager::default();
let pool = TxPool::new(impersonation.clone());
let pool = TxPool::new(impersonation.clone(), TransactionOrder::Fifo);
let tx_listener = pool.add_tx_listener();
InMemoryNode::new(
None,
Expand Down Expand Up @@ -1161,7 +1161,7 @@ impl InMemoryNode {
// TODO: Refactor InMemoryNode with a builder pattern
pub fn default_fork(fork: Option<ForkDetails>) -> Self {
let impersonation = ImpersonationManager::default();
let pool = TxPool::new(impersonation.clone());
let pool = TxPool::new(impersonation.clone(), TransactionOrder::Fifo);
let tx_listener = pool.add_tx_listener();
Self::new(
fork,
Expand Down Expand Up @@ -1263,7 +1263,10 @@ impl InMemoryNode {
tracing::debug!(count = txs.len(), "applying transactions");

// Create a temporary tx pool (i.e. state is not shared with the node mempool).
let pool = TxPool::new(self.impersonation.clone());
let pool = TxPool::new(
self.impersonation.clone(),
self.read_inner()?.config.transactions_order,
Romsters marked this conversation as resolved.
Show resolved Hide resolved
);
pool.add_txs(txs);

// Lock time so that the produced blocks are guaranteed to be sequential in time.
Expand Down Expand Up @@ -2032,7 +2035,7 @@ mod tests {
DEFAULT_ESTIMATE_GAS_SCALE_FACTOR, DEFAULT_FAIR_PUBDATA_PRICE, DEFAULT_L2_GAS_PRICE,
TEST_NODE_NETWORK_ID,
};
use anvil_zksync_config::types::SystemContractsOptions;
use anvil_zksync_config::types::{SystemContractsOptions, TransactionOrder};
use anvil_zksync_config::TestNodeConfig;
use ethabi::{Token, Uint};
use zksync_types::{utils::deployed_address_create, K256PrivateKey, Nonce};
Expand Down Expand Up @@ -2157,7 +2160,7 @@ mod tests {
raw_storage: external_storage.inner.read().unwrap().raw_storage.clone(),
};
let impersonation = ImpersonationManager::default();
let pool = TxPool::new(impersonation.clone());
let pool = TxPool::new(impersonation.clone(), TransactionOrder::Fifo);
let sealer = BlockSealer::new(BlockSealerMode::immediate(1000, pool.add_tx_listener()));
let node = InMemoryNode::new(
Some(ForkDetails {
Expand Down Expand Up @@ -2197,7 +2200,7 @@ mod tests {
#[tokio::test]
async fn test_transact_returns_data_in_built_in_without_security_mode() {
let impersonation = ImpersonationManager::default();
let pool = TxPool::new(impersonation.clone());
let pool = TxPool::new(impersonation.clone(), TransactionOrder::Fifo);
let sealer = BlockSealer::new(BlockSealerMode::immediate(1000, pool.add_tx_listener()));
let node = InMemoryNode::new(
None,
Expand Down
6 changes: 4 additions & 2 deletions crates/core/src/node/in_memory_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,8 @@ impl InMemoryNode {
}

pub fn remove_pool_transactions(&self, address: Address) -> Result<()> {
self.pool.drop_transactions_by_sender(address);
self.pool
.drop_transactions(|tx| tx.transaction.common_data.initiator_address == address);
Ok(())
}

Expand Down Expand Up @@ -499,6 +500,7 @@ mod tests {
use crate::node::time::{ReadTime, TimestampManager};
use crate::node::InMemoryNode;
use crate::node::{BlockSealer, ImpersonationManager, InMemoryNodeInner, Snapshot, TxPool};
use anvil_zksync_config::types::TransactionOrder;
use std::str::FromStr;
use std::sync::{Arc, RwLock};
use zksync_multivm::interface::storage::ReadStorage;
Expand Down Expand Up @@ -626,7 +628,7 @@ mod tests {
rich_accounts: Default::default(),
previous_states: Default::default(),
};
let pool = TxPool::new(impersonation.clone());
let pool = TxPool::new(impersonation.clone(), TransactionOrder::Fifo);
let sealer = BlockSealer::new(BlockSealerMode::immediate(1000, pool.add_tx_listener()));

let node = InMemoryNode {
Expand Down
Loading
Loading