Skip to content

Commit

Permalink
Merge branch 'main' into daniyar/refactor/jsonrpsee
Browse files Browse the repository at this point in the history
# Conflicts:
#	crates/cli/src/cli.rs
#	crates/config/src/types/mod.rs
#	crates/core/src/node/eth.rs
#	crates/core/src/node/in_memory.rs
#	e2e-tests-rust/tests/lib.rs
  • Loading branch information
itegulov committed Dec 18, 2024
2 parents d7f6147 + 4f35268 commit 345c492
Show file tree
Hide file tree
Showing 12 changed files with 356 additions and 87 deletions.
9 changes: 7 additions & 2 deletions crates/cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use anvil_zksync_config::constants::{
use anvil_zksync_config::types::{
AccountGenerator, CacheConfig, CacheType, Genesis, SystemContractsOptions,
};
use anvil_zksync_config::TestNodeConfig;
use anvil_zksync_config::{types::TransactionOrder, TestNodeConfig};
use anvil_zksync_types::{LogLevel, ShowCalls, ShowGasDetails, ShowStorageLogs, ShowVMDetails};
use clap::{arg, command, Parser, Subcommand};
use rand::{rngs::StdRng, SeedableRng};
Expand Down Expand Up @@ -251,6 +251,10 @@ pub struct Cli {
/// Disable CORS.
#[arg(long, conflicts_with = "allow_origin", help_heading = "Server options")]
pub no_cors: bool,

/// Transaction ordering in the mempool.
#[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_transaction_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 @@ -292,7 +292,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.transaction_order);
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 @@ -116,6 +116,8 @@ pub struct TestNodeConfig {
pub allow_origin: String,
/// Disable CORS if true
pub no_cors: bool,
/// How transactions are sorted in the mempool
pub transaction_order: TransactionOrder,
}

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

max_transactions: 1000,
transaction_order: TransactionOrder::Fifo,

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

// Set transactions order in the mempool
#[must_use]
pub fn with_transaction_order(mut self, transaction_order: TransactionOrder) -> Self {
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
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"),
}
}
}
30 changes: 19 additions & 11 deletions crates/core/src/console_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,33 @@ impl Default for ConsoleLogHandler {

impl ConsoleLogHandler {
pub fn handle_calls_recursive(&self, calls: &Vec<Call>) {
tracing::info!("");
tracing::info!("==== Console logs: ");

let mut messages: Vec<String> = vec![];
for call in calls {
self.handle_call_recursive(call);
self.handle_call_recursive(call, &mut messages);
}

if !messages.is_empty() {
tracing::info!("");
tracing::info!("==== Console logs: ");
}
for message in messages {
tracing::info!("{}", message.cyan());
}
}
pub fn handle_call_recursive(&self, current_call: &Call) {
self.handle_call(current_call);
pub fn handle_call_recursive(&self, current_call: &Call, messages: &mut Vec<String>) {
if let Some(message) = self.handle_call(current_call) {
messages.push(message);
};
for call in &current_call.calls {
self.handle_call_recursive(call);
self.handle_call_recursive(call, messages);
}
}
pub fn handle_call(&self, current_call: &Call) {
pub fn handle_call(&self, current_call: &Call) -> Option<String> {
if current_call.to != self.target_contract {
return;
return None;
}
if current_call.input.len() < 4 {
return;
return None;
}
let signature = &current_call.input[..4];
let message =
Expand All @@ -67,7 +75,7 @@ impl ConsoleLogHandler {
tokens.iter().map(|t| format!("{}", t)).join(" ")
})
});
tracing::info!("{}", message.cyan());
Some(message)
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/core/src/node/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,15 +380,15 @@ impl InMemoryNode {
Ok(reader
.tx_results
.get(&hash)
.and_then(|TransactionResult { info, .. }| {
.and_then(|TransactionResult { info, receipt, .. }| {
let input_data = info.tx.common_data.input.clone().or(None)?;
let chain_id = info.tx.common_data.extract_chain_id().or(None)?;
Some(zksync_types::api::Transaction {
hash,
nonce: U256::from(info.tx.common_data.nonce.0),
block_hash: Some(hash),
block_number: Some(U64::from(info.miniblock_number)),
transaction_index: Some(U64::from(0)),
transaction_index: Some(receipt.transaction_index),
from: Some(info.tx.initiator_account()),
to: info.tx.recipient_account(),
value: info.tx.execute.value,
Expand Down
34 changes: 20 additions & 14 deletions crates/core/src/node/in_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1101,7 +1101,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 @@ -1154,7 +1154,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 @@ -1256,7 +1256,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.transaction_order,
);
pool.add_txs(txs);

// Lock time so that the produced blocks are guaranteed to be sequential in time.
Expand Down Expand Up @@ -1648,6 +1651,7 @@ impl InMemoryNode {
pub fn run_l2_tx<W: WriteStorage, H: HistoryMode>(
&self,
l2_tx: L2Tx,
l2_tx_index: U64,
block_ctx: &BlockContext,
batch_env: &L1BatchEnv,
vm: &mut Vm<W, H>,
Expand Down Expand Up @@ -1723,7 +1727,7 @@ impl InMemoryNode {
block_number: Some(block_ctx.miniblock.into()),
l1_batch_number: Some(U64::from(batch_env.number.0)),
transaction_hash: Some(tx_hash),
transaction_index: Some(U64::zero()),
transaction_index: Some(l2_tx_index),
log_index: Some(U256::from(log_idx)),
transaction_log_index: Some(U256::from(log_idx)),
log_type: None,
Expand All @@ -1738,7 +1742,7 @@ impl InMemoryNode {
}
let tx_receipt = TransactionReceipt {
transaction_hash: tx_hash,
transaction_index: U64::from(0),
transaction_index: l2_tx_index,
block_hash: block_ctx.hash,
block_number: block_ctx.miniblock.into(),
l1_batch_tx_index: None,
Expand Down Expand Up @@ -1804,18 +1808,20 @@ impl InMemoryNode {

// Execute transactions and bootloader
let mut executed_tx_hashes = Vec::with_capacity(tx_hashes.len());
let mut tx_index = U64::from(0);
for tx in txs {
// Executing a next transaction means that a previous transaction was either rolled back (in which case its snapshot
// was already removed), or that we build on top of it (in which case, it can be removed now).
vm.pop_snapshot_no_rollback();
// Save pre-execution VM snapshot.
vm.make_snapshot();
let hash = tx.hash();
if let Err(e) = self.run_l2_tx(tx, &block_ctx, &batch_env, &mut vm) {
if let Err(e) = self.run_l2_tx(tx, tx_index, &block_ctx, &batch_env, &mut vm) {
tracing::error!("Error while executing transaction: {e}");
vm.rollback_to_the_latest_snapshot();
} else {
executed_tx_hashes.push(hash);
tx_index += U64::from(1);
}
}
vm.execute(InspectExecutionMode::Bootloader);
Expand All @@ -1832,7 +1838,7 @@ impl InMemoryNode {
let mut transactions = Vec::new();
let mut tx_receipts = Vec::new();
let mut debug_calls = Vec::new();
for tx_hash in &executed_tx_hashes {
for (index, tx_hash) in executed_tx_hashes.iter().enumerate() {
let Some(tx_result) = inner.tx_results.get(tx_hash) else {
// Skipping halted transaction
continue;
Expand All @@ -1843,7 +1849,7 @@ impl InMemoryNode {
let mut transaction = zksync_types::api::Transaction::from(tx_result.info.tx.clone());
transaction.block_hash = Some(block_ctx.hash);
transaction.block_number = Some(U64::from(block_ctx.miniblock));
transaction.transaction_index = Some(Index::zero());
transaction.transaction_index = Some(index.into());
transaction.l1_batch_number = Some(U64::from(batch_env.number.0));
transaction.l1_batch_tx_index = Some(Index::zero());
if transaction.transaction_type == Some(U64::zero())
Expand Down Expand Up @@ -2116,7 +2122,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 +2163,7 @@ mod tests {
.unwrap();
let (block_ctx, batch_env, mut vm) = test_vm(&node, system_contracts.clone());
let err = node
.run_l2_tx(tx, &block_ctx, &batch_env, &mut vm)
.run_l2_tx(tx, U64::from(0), &block_ctx, &batch_env, &mut vm)
.unwrap_err();
assert_eq!(err.to_string(), "exceeds block gas limit");
}
Expand All @@ -2178,7 +2184,7 @@ mod tests {
.unwrap();
let (block_ctx, batch_env, mut vm) = test_vm(&node, system_contracts.clone());
let err = node
.run_l2_tx(tx, &block_ctx, &batch_env, &mut vm)
.run_l2_tx(tx, U64::from(0), &block_ctx, &batch_env, &mut vm)
.unwrap_err();

assert_eq!(
Expand All @@ -2203,7 +2209,7 @@ mod tests {
.unwrap();
let (block_ctx, batch_env, mut vm) = test_vm(&node, system_contracts.clone());
let err = node
.run_l2_tx(tx, &block_ctx, &batch_env, &mut vm)
.run_l2_tx(tx, U64::from(0), &block_ctx, &batch_env, &mut vm)
.unwrap_err();

assert_eq!(
Expand Down Expand Up @@ -2241,7 +2247,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 @@ -2281,7 +2287,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 @@ -440,7 +440,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 @@ -497,6 +498,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 @@ -633,7 +635,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

0 comments on commit 345c492

Please sign in to comment.