Skip to content

Commit

Permalink
feat: Add Canyon support (#89)
Browse files Browse the repository at this point in the history
* fork at timestamp

* add support for different gas constants

* add deposit nonce to receipt

* enable Canyon execution

* enable Canyon derivation

* fallback to Regolith

* switch Bedrock update to timestamp

* fix spec in derivation

* add canyon test block

* update copyright
  • Loading branch information
Wollac committed Feb 13, 2024
1 parent f0384a4 commit e20188d
Show file tree
Hide file tree
Showing 17 changed files with 305 additions and 188 deletions.
6 changes: 2 additions & 4 deletions guests/op-derive/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,13 @@
#![no_main]

use risc0_zkvm::guest::env;
use zeth_lib::optimism::{
batcher_db::MemDb, config::OPTIMISM_CHAIN_SPEC, DeriveInput, DeriveMachine,
};
use zeth_lib::optimism::{batcher_db::MemDb, config::ChainConfig, DeriveInput, DeriveMachine};

risc0_zkvm::guest::entry!(main);

pub fn main() {
let derive_input: DeriveInput<MemDb> = env::read();
let mut derive_machine = DeriveMachine::new(&OPTIMISM_CHAIN_SPEC, derive_input, None)
let mut derive_machine = DeriveMachine::new(ChainConfig::optimism(), derive_input, None)
.expect("Could not create derive machine");
let output = derive_machine
.derive(None)
Expand Down
19 changes: 11 additions & 8 deletions host/src/operations/rollups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use zeth_lib::{
optimism::{
batcher_db::BatcherDb,
composition::{ComposeInput, ComposeInputOperation, ComposeOutputOperation},
config::OPTIMISM_CHAIN_SPEC,
config::ChainConfig,
DeriveInput, DeriveMachine,
},
output::BlockBuildOutput,
Expand All @@ -52,8 +52,10 @@ pub async fn derive_rollup_blocks(cli: &Cli) -> anyhow::Result<Option<(String, R
);

info!("Running preflight");
let config = ChainConfig::optimism();
let derive_input = DeriveInput {
db: RpcDb::new(
&config,
build_args.eth_rpc_url.clone(),
build_args.op_rpc_url.clone(),
build_args.cache.clone(),
Expand All @@ -65,9 +67,8 @@ pub async fn derive_rollup_blocks(cli: &Cli) -> anyhow::Result<Option<(String, R
};
let factory_clone = op_builder_provider_factory.clone();
let (op_block_inputs, derive_machine, derive_output) = tokio::task::spawn_blocking(move || {
let mut derive_machine =
DeriveMachine::new(&OPTIMISM_CHAIN_SPEC, derive_input, Some(factory_clone))
.expect("Could not create derive machine");
let mut derive_machine = DeriveMachine::new(config, derive_input, Some(factory_clone))
.expect("Could not create derive machine");
let mut op_block_inputs = vec![];
let derive_output = derive_machine
.derive(Some(&mut op_block_inputs))
Expand All @@ -92,13 +93,13 @@ pub async fn derive_rollup_blocks(cli: &Cli) -> anyhow::Result<Option<(String, R
let input_clone = derive_input_mem.clone();
let output_mem = tokio::task::spawn_blocking(move || {
DeriveMachine::new(
&OPTIMISM_CHAIN_SPEC,
ChainConfig::optimism(),
input_clone,
Some(op_builder_provider_factory),
)
.expect("Could not create derive machine")
.derive(None)
.unwrap()
.expect("could not derive")
})
.await?;
assert_eq!(derive_output, output_mem);
Expand Down Expand Up @@ -153,7 +154,9 @@ pub async fn compose_derived_rollup_blocks(
let mut lift_queue = Vec::new();
let mut complete_eth_chain: Vec<Header> = Vec::new();
for op_block_index in (0..build_args.block_count).step_by(composition_size as usize) {
let config = ChainConfig::optimism();
let db = RpcDb::new(
&config,
build_args.eth_rpc_url.clone(),
build_args.op_rpc_url.clone(),
build_args.cache.clone(),
Expand All @@ -173,7 +176,7 @@ pub async fn compose_derived_rollup_blocks(
};
let factory_clone = op_builder_provider_factory.clone();
let mut derive_machine = tokio::task::spawn_blocking(move || {
DeriveMachine::new(&OPTIMISM_CHAIN_SPEC, derive_input, Some(factory_clone))
DeriveMachine::new(config, derive_input, Some(factory_clone))
.expect("Could not create derive machine")
})
.await?;
Expand Down Expand Up @@ -232,7 +235,7 @@ pub async fn compose_derived_rollup_blocks(
let input_clone = derive_input_mem.clone();
let output_mem = tokio::task::spawn_blocking(move || {
DeriveMachine::new(
&OPTIMISM_CHAIN_SPEC,
ChainConfig::optimism(),
input_clone,
Some(op_builder_provider_factory),
)
Expand Down
Binary file added host/testdata/optimism/115892782.json.gz
Binary file not shown.
12 changes: 1 addition & 11 deletions lib/src/builder/execute/ethereum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ use zeth_primitives::{
use super::TxExecStrategy;
use crate::{builder::BlockBuilder, consts, guest_mem_forget};

/// Minimum supported protocol version: Paris (Block no. 15537394).
const MIN_SPEC_ID: SpecId = SpecId::MERGE;

pub struct EthTxExecStrategy {}

impl TxExecStrategy<EthereumTxEssence> for EthTxExecStrategy {
Expand All @@ -50,18 +47,11 @@ impl TxExecStrategy<EthereumTxEssence> for EthTxExecStrategy {
D: Database + DatabaseCommit,
<D as Database>::Error: Debug,
{
let spec_id = block_builder.spec_id.expect("Spec ID is not initialized");
let header = block_builder
.header
.as_mut()
.expect("Header is not initialized");
// Compute the spec id
let spec_id = block_builder.chain_spec.spec_id(header.number);
if !SpecId::enabled(spec_id, MIN_SPEC_ID) {
panic!(
"Invalid protocol version: expected >= {:?}, got {:?}",
MIN_SPEC_ID, spec_id,
)
}

#[cfg(not(target_os = "zkvm"))]
{
Expand Down
44 changes: 25 additions & 19 deletions lib/src/builder/execute/optimism.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use core::{fmt::Debug, mem::take};

use anyhow::{anyhow, bail, Context, Result};
#[cfg(not(target_os = "zkvm"))]
use log::{debug, trace};
use log::trace;
use revm::{
interpreter::Host,
optimism,
Expand All @@ -32,16 +32,13 @@ use zeth_primitives::{
optimism::{OptimismTxEssence, TxEssenceOptimismDeposited},
TxEssence,
},
trie::MptNode,
trie::{MptNode, EMPTY_ROOT},
Bloom, Bytes,
};

use super::{ethereum, TxExecStrategy};
use crate::{builder::BlockBuilder, consts, guest_mem_forget};

/// Minimum supported protocol version: Bedrock (Block no. 105235063).
const MIN_SPEC_ID: SpecId = SpecId::BEDROCK;

pub struct OpTxExecStrategy {}

impl TxExecStrategy<OptimismTxEssence> for OpTxExecStrategy {
Expand All @@ -52,19 +49,11 @@ impl TxExecStrategy<OptimismTxEssence> for OpTxExecStrategy {
D: Database + DatabaseCommit,
<D as Database>::Error: Debug,
{
let spec_id = block_builder.spec_id.expect("Spec ID is not initialized");
let header = block_builder
.header
.as_mut()
.expect("Header is not initialized");
// Compute the spec id
let spec_id = block_builder.chain_spec.spec_id(header.number);
if !SpecId::enabled(spec_id, MIN_SPEC_ID) {
panic!(
"Invalid protocol version: expected >= {:?}, got {:?}",
MIN_SPEC_ID, spec_id,
)
}
let chain_id = block_builder.chain_spec.chain_id();

#[cfg(not(target_os = "zkvm"))]
{
Expand All @@ -81,9 +70,9 @@ impl TxExecStrategy<OptimismTxEssence> for OpTxExecStrategy {
)
.unwrap();

debug!("Block no. {}", header.number);
debug!(" EVM spec ID: {:?}", spec_id);
debug!(" Timestamp: {}", dt);
trace!("Block no. {}", header.number);
trace!(" EVM spec ID: {:?}", spec_id);
trace!(" Timestamp: {}", dt);
trace!(
" Transactions: {}",
block_builder.input.state_input.transactions.len()
Expand All @@ -100,6 +89,7 @@ impl TxExecStrategy<OptimismTxEssence> for OpTxExecStrategy {
);
}

let chain_id = block_builder.chain_spec.chain_id();
let mut evm = Evm::builder()
.spec_id(spec_id)
.modify_cfg_env(|cfg_env| {
Expand Down Expand Up @@ -154,6 +144,15 @@ impl TxExecStrategy<OptimismTxEssence> for OpTxExecStrategy {
bail!("Error at transaction {}: gas exceeds block limit", tx_no);
}

// cache account nonce if the transaction is a deposit, starting with Canyon
let deposit_nonce = (spec_id >= SpecId::CANYON
&& matches!(tx.essence, OptimismTxEssence::OptimismDeposited(_)))
.then(|| {
let db = &mut evm.context.evm.db;
let account = db.basic(tx_from).expect("Depositor account not found");
account.unwrap_or_default().nonce
});

match &tx.essence {
OptimismTxEssence::OptimismDeposited(deposit) => {
#[cfg(not(target_os = "zkvm"))]
Expand Down Expand Up @@ -185,12 +184,15 @@ impl TxExecStrategy<OptimismTxEssence> for OpTxExecStrategy {
trace!(" Ok: {:?}", result);

// create the receipt from the EVM result
let receipt = Receipt::new(
let mut receipt = Receipt::new(
tx.essence.tx_type(),
result.is_success(),
cumulative_gas_used,
result.logs().into_iter().map(|log| log.into()).collect(),
);
if let Some(nonce) = deposit_nonce {
receipt = receipt.with_deposit_nonce(nonce);
}

// update account states
#[cfg(not(target_os = "zkvm"))]
Expand Down Expand Up @@ -244,7 +246,11 @@ impl TxExecStrategy<OptimismTxEssence> for OpTxExecStrategy {
header.receipts_root = receipt_trie.hash();
header.logs_bloom = logs_bloom;
header.gas_used = cumulative_gas_used;
header.withdrawals_root = None;
header.withdrawals_root = if spec_id < SpecId::CANYON {
None
} else {
Some(EMPTY_ROOT)
};

// Leak memory, save cycles
guest_mem_forget([tx_trie, receipt_trie]);
Expand Down
4 changes: 3 additions & 1 deletion lib/src/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
use std::sync::{Arc, Mutex};

use anyhow::Result;
use revm::{Database, DatabaseCommit};
use revm::{primitives::SpecId, Database, DatabaseCommit};
use serde::Serialize;
use zeth_primitives::{
block::Header,
Expand Down Expand Up @@ -53,6 +53,7 @@ pub struct BlockBuilder<'a, D, E: TxEssence> {
pub(crate) chain_spec: &'a ChainSpec,
pub(crate) input: BlockBuildInput<E>,
pub(crate) db: Option<D>,
pub(crate) spec_id: Option<SpecId>,
pub(crate) header: Option<Header>,
pub db_drop_destination: Option<DatabaseRescue<D>>,
}
Expand Down Expand Up @@ -86,6 +87,7 @@ where
BlockBuilder {
chain_spec,
db: None,
spec_id: None,
header: None,
input,
db_drop_destination: db_backup,
Expand Down
21 changes: 16 additions & 5 deletions lib/src/builder/prepare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,8 @@ impl HeaderPrepStrategy for EthHeaderPrepStrategy {
);
}
// Validate timestamp
if block_builder.input.state_input.timestamp
<= block_builder.input.state_input.parent_header.timestamp
{
let timestamp = block_builder.input.state_input.timestamp;
if timestamp <= block_builder.input.state_input.parent_header.timestamp {
bail!(
"Invalid timestamp: expected > {}, got {}",
block_builder.input.state_input.parent_header.timestamp,
Expand All @@ -83,6 +82,18 @@ impl HeaderPrepStrategy for EthHeaderPrepStrategy {
extra_data_bytes,
)
}
// Validate number
let parent_number = block_builder.input.state_input.parent_header.number;
let number = parent_number
.checked_add(1)
.context("Invalid number: too large")?;

// Derive fork version
let spec_id = block_builder
.chain_spec
.active_fork(number, &timestamp)
.unwrap_or_else(|err| panic!("Invalid version: {:#}", err));
block_builder.spec_id = Some(spec_id);
// Derive header
block_builder.header = Some(Header {
// Initialize fields that we can compute from the parent
Expand All @@ -96,12 +107,12 @@ impl HeaderPrepStrategy for EthHeaderPrepStrategy {
.context("Invalid block number: too large")?,
base_fee_per_gas: derive_base_fee(
&block_builder.input.state_input.parent_header,
block_builder.chain_spec.gas_constants(),
block_builder.chain_spec.gas_constants(spec_id).unwrap(),
),
// Initialize metadata from input
beneficiary: block_builder.input.state_input.beneficiary,
gas_limit: block_builder.input.state_input.gas_limit,
timestamp: block_builder.input.state_input.timestamp,
timestamp,
mix_hash: block_builder.input.state_input.mix_hash,
extra_data: block_builder.input.state_input.extra_data.clone(),
// do not fill the remaining fields
Expand Down
Loading

0 comments on commit e20188d

Please sign in to comment.