Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
817 changes: 392 additions & 425 deletions Cargo.lock

@duyquang6 duyquang6 May 28, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

When update sha3-asm to 0.1.7, performance regressed 15% on bench mainnet, root caused:

  • 0.1.6: SHA3_absorb — loads keccak state into GP registers (x0-x28), runs scalar keccak
  • 0.1.7: SHA3_absorb_cext — loads state into FP/NEON registers (d0-d24), runs using eor3/rax1/xar/bcax SHA3 crypto extension instructions

Despite SHA3 instructions being faster per-round on paper, scalar version wins on Graviton. Likely reasons:

  • EVM keccak calls are short (32-byte hash), setup overhead of NEON register loads dominates
  • Graviton3 has better scalar pipeline utilization for this access pattern
  • GP register file avoids FP register bank crossing penalty

Worth reporting to crate author

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@duyquang6 duyquang6 May 28, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Large diffs are not rendered by default.

18 changes: 14 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,9 @@ alloy-transport = "2.0.1"
alloy-trie = "0.9.5"

# Will remove [revm] with https://github.com/risechain/pevm/issues/382.
op-revm = "19.0.0"
revm = { version = "38.0.0", features = ["serde"] }
revm-statetest-types = "17.0.1"
revme = "15.0.0"
revm = { version = "39.0.0", features = ["serde"] }
revm-statetest-types = "18.0.0"
revme = "16.0.0"

# OP
op-alloy-consensus = "2.0.0"
Expand Down Expand Up @@ -88,3 +87,14 @@ smallvec = "1.15.1"
thiserror = "2.0.18"
tokio = { version = "1.52.1", features = ["rt-multi-thread"] }
walkdir = "2.5.0"

[patch.crates-io]
# revm-database-interface 11.1.0 and several companion crates were yanked from crates.io.
# The git source (tag v108) declares the same version numbers so cargo's semver resolver is
# satisfied without any API change. We patch the whole set to avoid duplicate-crate ambiguities
# that arise when mixing crates.io and git sources for interdependent revm subcrates.
revm-bytecode = { git = "https://github.com/bluealloy/revm", tag = "v108" }
revm-database = { git = "https://github.com/bluealloy/revm", tag = "v108" }
revm-database-interface = { git = "https://github.com/bluealloy/revm", tag = "v108" }
revm-primitives = { git = "https://github.com/bluealloy/revm", tag = "v108" }
revm-state = { git = "https://github.com/bluealloy/revm", tag = "v108" }
1 change: 0 additions & 1 deletion crates/pevm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ serde.workspace = true
smallvec.workspace = true
thiserror.workspace = true

op-revm.workspace = true
revm.workspace = true

op-alloy-consensus.workspace = true
Expand Down
93 changes: 34 additions & 59 deletions crates/pevm/src/chain/rise.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
//! RISE
use std::sync::LazyLock;

use crate::rise_revm::{
BASE_FEE_RECIPIENT, RiseEvm, RiseHaltReason, RiseTransaction, RiseTransactionError,
transaction::DepositTransactionParts,
};
use alloy_consensus::Transaction;
use alloy_primitives::{Address, B256, ChainId, U256};
use alloy_rpc_types_eth::{BlockTransactions, Header};
use hashbrown::HashMap;
use op_alloy_consensus::{OpDepositReceipt, OpReceiptEnvelope, OpTxEnvelope, OpTxType};
use op_alloy_network::eip2718::Encodable2718;
use op_revm::{
L1BlockInfo, OpBuilder, OpContext, OpEvm, OpHaltReason, OpSpecId, OpTransaction,
OpTransactionError,
constants::{BASE_FEE_RECIPIENT, L1_FEE_RECIPIENT, OPERATOR_FEE_RECIPIENT},
transaction::{OpTxTr, deposit::DepositTransactionParts},
};
use revm::{
Context, Database, MainContext,
context::{BlockEnv, CfgEnv, TxEnv},
context_interface::either::Either,
handler::EvmTr,
primitives::hardfork::SpecId,
};
use smallvec::SmallVec;

Expand All @@ -33,12 +32,6 @@ const RISE_CHAIN_ID: ChainId = 4153; // Mainnet
static BASE_FEE_RECIPIENT_LOCATION_HASH: LazyLock<MemoryLocationHash> =
LazyLock::new(|| hash_deterministic(MemoryLocation::Basic(BASE_FEE_RECIPIENT)));

static L1_FEE_RECIPIENT_LOCATION_HASH: LazyLock<MemoryLocationHash> =
LazyLock::new(|| hash_deterministic(MemoryLocation::Basic(L1_FEE_RECIPIENT)));

static OPERATOR_FEE_RECIPIENT_LOCATION_HASH: LazyLock<MemoryLocationHash> =
LazyLock::new(|| hash_deterministic(MemoryLocation::Basic(OPERATOR_FEE_RECIPIENT)));

/// Implementation of [`PevmChain`] for RISE
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PevmRise;
Expand All @@ -65,11 +58,11 @@ impl PevmChain for PevmRise {
type Network = op_alloy_network::Optimism;
type Transaction = op_alloy_rpc_types::Transaction;
type Envelope = OpTxEnvelope;
type Evm<DB: Database> = OpEvm<OpContext<DB>, ()>;
type EvmSpecId = OpSpecId;
type EvmTx = OpTransaction<TxEnv>;
type EvmHaltReason = OpHaltReason;
type EvmErrorType = OpTransactionError;
type Evm<DB: Database> = RiseEvm<DB>;
type EvmSpecId = SpecId;
type EvmTx = RiseTransaction;
type EvmHaltReason = RiseHaltReason;
type EvmErrorType = RiseTransactionError;
type BlockSpecError = std::convert::Infallible;
type TransactionParsingError = RiseTransactionParsingError;

Expand All @@ -85,9 +78,8 @@ impl PevmChain for PevmRise {
}
}

fn get_block_spec(&self, _header: &Header) -> Result<OpSpecId, Self::BlockSpecError> {
// RISE Mainnet launched as JOVIAN; currently all blocks use this spec.
Ok(OpSpecId::JOVIAN)
fn get_block_spec(&self, _header: &Header) -> Result<SpecId, Self::BlockSpecError> {
Ok(SpecId::PRAGUE)
}

fn build_evm<DB: Database>(
Expand All @@ -96,16 +88,17 @@ impl PevmChain for PevmRise {
block_env: BlockEnv,
db: DB,
) -> Self::Evm<DB> {
Context::mainnet()
.with_cfg(CfgEnv::new_with_spec(spec_id).with_chain_id(RISE_CHAIN_ID))
.with_block(block_env)
.with_db(db)
.with_tx(OpTransaction::default())
.with_chain(L1BlockInfo::default())
.build_op()
RiseEvm::new(
Context::mainnet()
.with_cfg(CfgEnv::new_with_spec(spec_id).with_chain_id(RISE_CHAIN_ID))
.with_block(block_env)
.with_db(db)
.with_tx(RiseTransaction::default())
.with_chain(()),
)
}

fn build_mv_memory(&self, block_env: &BlockEnv, txs: &[OpTransaction<TxEnv>]) -> MvMemory {
fn build_mv_memory(&self, block_env: &BlockEnv, txs: &[RiseTransaction]) -> MvMemory {
let beneficiary_location_hash =
hash_deterministic(MemoryLocation::Basic(block_env.beneficiary));

Expand All @@ -125,26 +118,13 @@ impl PevmChain for PevmRise {
.entry(*BASE_FEE_RECIPIENT_LOCATION_HASH)
.or_insert_with(|| Vec::with_capacity(txs.len()))
.push(index);
estimated_locations
.entry(*L1_FEE_RECIPIENT_LOCATION_HASH)
.or_insert_with(|| Vec::with_capacity(txs.len()))
.push(index);
estimated_locations
.entry(*OPERATOR_FEE_RECIPIENT_LOCATION_HASH)
.or_insert_with(|| Vec::with_capacity(txs.len()))
.push(index);
}
}

MvMemory::new(
txs.len(),
estimated_locations,
[
block_env.beneficiary,
BASE_FEE_RECIPIENT,
L1_FEE_RECIPIENT,
OPERATOR_FEE_RECIPIENT,
],
[block_env.beneficiary, BASE_FEE_RECIPIENT],
)
}

Expand All @@ -168,11 +148,6 @@ impl PevmChain for PevmRise {
*BASE_FEE_RECIPIENT_LOCATION_HASH,
U256::from(basefee).saturating_mul(gas_used),
),
// RISE disables DA footprint and operator fees. Annoyingly, we still
// need to touch these to match revm's sequential execution for now.
// Will remove once we rewrite our own EVM implementation.
(*L1_FEE_RECIPIENT_LOCATION_HASH, U256::ZERO),
(*OPERATOR_FEE_RECIPIENT_LOCATION_HASH, U256::ZERO),
]
}
}
Expand All @@ -182,7 +157,7 @@ impl PevmChain for PevmRise {
// https://github.com/paradigmxyz/reth/blob/b4a1b733c93f7e262f1b774722670e08cdcb6276/crates/primitives/src/proofs.rs
fn calculate_receipt_root(
&self,
_: OpSpecId,
_: SpecId,
txs: &BlockTransactions<Self::Transaction>,
tx_results: &[PevmTxExecutionResult],
) -> Result<B256, CalculateReceiptRootError> {
Expand Down Expand Up @@ -232,8 +207,8 @@ impl PevmChain for PevmRise {
fn get_tx_env(
&self,
tx: &Self::Transaction,
) -> Result<OpTransaction<TxEnv>, RiseTransactionParsingError> {
Ok(OpTransaction {
) -> Result<RiseTransaction, RiseTransactionParsingError> {
Ok(RiseTransaction {
base: TxEnv {
tx_type: tx.inner.inner.tx_type().into(),
caller: tx.inner.inner.signer(),
Expand All @@ -259,18 +234,18 @@ impl PevmChain for PevmRise {
Some(tx.inner.inner.encoded_2718().into())
},
deposit: if let Some(deposit) = tx.inner.inner.as_deposit() {
DepositTransactionParts::new(
deposit.source_hash,
Some(deposit.mint),
deposit.is_system_transaction,
)
DepositTransactionParts {
source_hash: deposit.source_hash,
mint: Some(deposit.mint),
is_system_transaction: deposit.is_system_transaction,
}
} else {
DepositTransactionParts::new(B256::ZERO, None, false)
DepositTransactionParts::default()
},
})
}

fn tx_env<'a>(&self, tx: &'a OpTransaction<TxEnv>) -> &'a TxEnv {
fn tx_env<'a>(&self, tx: &'a RiseTransaction) -> &'a TxEnv {
&tx.base
}

Expand All @@ -281,11 +256,11 @@ impl PevmChain for PevmRise {
!is_deposit
}

fn is_eip_1559_enabled(&self, _: OpSpecId) -> bool {
fn is_eip_1559_enabled(&self, _: SpecId) -> bool {
true
}

fn is_eip_161_enabled(&self, _: OpSpecId) -> bool {
fn is_eip_161_enabled(&self, _: SpecId) -> bool {
true
}
}
1 change: 1 addition & 0 deletions crates/pevm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ pub mod chain;
mod compat;
mod mv_memory;
mod pevm;
pub(crate) mod rise_revm;
pub use pevm::{Pevm, PevmError, PevmResult, execute_revm_sequential};
mod scheduler;
mod storage;
Expand Down
122 changes: 122 additions & 0 deletions crates/pevm/src/rise_revm/evm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use super::precompiles::RisePrecompiles;
use super::{
RiseContext, RiseHaltReason, RiseTransaction, RiseTransactionError, handler::RiseHandler,
};
use revm::{
Database, ExecuteEvm,
context::{BlockEnv, ContextError, ContextSetters, Evm, FrameStack},
context_interface::{
ContextTr,
result::{EVMError, ExecResultAndState, ExecutionResult},
},
handler::{
EthFrame, EvmTr, FrameInitOrResult, FrameResult, Handler, ItemOrResult, evm::FrameTr,
instructions::EthInstructions,
},
interpreter::interpreter::EthInterpreter,
state::EvmState,
};

pub(crate) type RiseError<DB> = EVMError<<DB as Database>::Error, RiseTransactionError>;

/// RISE EVM wrapping [`Evm`] with RISE-specific precompiles and handler dispatch.
#[derive(Debug)]
#[allow(clippy::type_complexity)]
pub struct RiseEvm<DB: Database>(
Evm<
RiseContext<DB>,
(),
EthInstructions<EthInterpreter, RiseContext<DB>>,
RisePrecompiles,
EthFrame<EthInterpreter>,
>,
);

impl<DB: Database> RiseEvm<DB> {
pub(crate) fn new(ctx: RiseContext<DB>) -> Self {
let spec = *ctx.cfg().spec();
Self(Evm {
ctx,
inspector: (),
instruction: EthInstructions::new_mainnet_with_spec(spec),
precompiles: RisePrecompiles::default(),
frame_stack: FrameStack::new_prealloc(8),
})
}
}

impl<DB: Database> EvmTr for RiseEvm<DB> {
type Context = RiseContext<DB>;
type Instructions = EthInstructions<EthInterpreter, RiseContext<DB>>;
type Precompiles = RisePrecompiles;
type Frame = EthFrame<EthInterpreter>;

fn all(
&self,
) -> (
&Self::Context,
&Self::Instructions,
&Self::Precompiles,
&FrameStack<Self::Frame>,
) {
self.0.all()
}

fn all_mut(
&mut self,
) -> (
&mut Self::Context,
&mut Self::Instructions,
&mut Self::Precompiles,
&mut FrameStack<Self::Frame>,
) {
self.0.all_mut()
}

fn frame_init(
&mut self,
frame_input: <Self::Frame as FrameTr>::FrameInit,
) -> Result<ItemOrResult<&mut Self::Frame, FrameResult>, ContextError<DB::Error>> {
self.0.frame_init(frame_input)
}

fn frame_run(&mut self) -> Result<FrameInitOrResult<Self::Frame>, ContextError<DB::Error>> {
self.0.frame_run()
}

fn frame_return_result(
&mut self,
result: FrameResult,
) -> Result<Option<FrameResult>, ContextError<DB::Error>> {
self.0.frame_return_result(result)
}
}

impl<DB: Database> ExecuteEvm for RiseEvm<DB> {
type Tx = RiseTransaction;
type Block = BlockEnv;
type State = EvmState;
type Error = RiseError<DB>;
type ExecutionResult = ExecutionResult<RiseHaltReason>;

fn set_block(&mut self, block: Self::Block) {
self.0.ctx.set_block(block);
}

fn transact_one(&mut self, tx: Self::Tx) -> Result<Self::ExecutionResult, Self::Error> {
self.0.ctx.set_tx(tx);
RiseHandler::default().run(self)
}

fn finalize(&mut self) -> Self::State {
self.0.ctx.journal_mut().finalize()
}

fn replay(
&mut self,
) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
RiseHandler::default()
.run(self)
.map(|result| ExecResultAndState::new(result, self.finalize()))
}
}
Loading
Loading