Skip to content

Commit

Permalink
feat(executor): EIP-1559 configurability spec updates
Browse files Browse the repository at this point in the history
  • Loading branch information
clabby committed Oct 19, 2024
1 parent 7d22a13 commit 17c6c15
Show file tree
Hide file tree
Showing 5 changed files with 260 additions and 78 deletions.
13 changes: 13 additions & 0 deletions bin/client/src/fault/handler/bn128_pair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ const PAIR_ELEMENT_LEN: usize = 64 + 128;
pub(crate) const FPVM_ECPAIRING: PrecompileWithAddress =
PrecompileWithAddress(ECPAIRING_ADDRESS, Precompile::Standard(fpvm_ecpairing));

pub(crate) const FPVM_ECPAIRING_GRANITE: PrecompileWithAddress =
PrecompileWithAddress(ECPAIRING_ADDRESS, Precompile::Standard(fpvm_ecpairing_granite));

/// Performs an FPVM-accelerated `ecpairing` precompile call.
fn fpvm_ecpairing(input: &Bytes, gas_limit: u64) -> PrecompileResult {
let gas_used =
Expand Down Expand Up @@ -59,3 +62,13 @@ fn fpvm_ecpairing(input: &Bytes, gas_limit: u64) -> PrecompileResult {

Ok(PrecompileOutput::new(gas_used, result_data.into()))
}

/// Performs an FPVM-accelerated `ecpairing` precompile call after the Granite hardfork.
fn fpvm_ecpairing_granite(input: &Bytes, gas_limit: u64) -> PrecompileResult {
const BN256_MAX_PAIRING_SIZE_GRANITE: usize = 112_687;
if input.len() > BN256_MAX_PAIRING_SIZE_GRANITE {
return Err(PrecompileError::Bn128PairLength.into());
}

fpvm_ecpairing(input, gas_limit)
}
5 changes: 5 additions & 0 deletions bin/client/src/fault/handler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ pub(crate) fn fpvm_handle_register<F, H>(
kzg_point_eval::FPVM_KZG_POINT_EVAL,
];
ctx_precompiles.extend(override_precompiles);

if spec_id.is_enabled_in(SpecId::GRANITE) {
ctx_precompiles.extend([bn128_pair::FPVM_ECPAIRING_GRANITE]);
}

ctx_precompiles
});
}
6 changes: 6 additions & 0 deletions crates/executor/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,18 @@ pub enum ExecutorError {
/// Missing gas limit in the payload attributes.
#[error("Gas limit not provided in payload attributes")]
MissingGasLimit,
/// Missing transactions in the payload attributes.
#[error("Transactions not provided in payload attributes")]
MissingTransactions,
/// Missing EIP-1559 parameters in execution payload post-Holocene.
#[error("Missing EIP-1559 parameters in execution payload post-Holocene")]
MissingEIP1559Params,
/// Missing parent beacon block root in the payload attributes.
#[error("Parent beacon block root not provided in payload attributes")]
MissingParentBeaconBlockRoot,
/// Invalid `extraData` field in the block header.
#[error("Invalid `extraData` field in the block header")]
InvalidExtraData,
/// Block gas limit exceeded.
#[error("Block gas limit exceeded")]
BlockGasLimitExceeded,
Expand Down
110 changes: 34 additions & 76 deletions crates/executor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use alloy_eips::{
eip1559::BaseFeeParams,
eip2718::{Decodable2718, Encodable2718},
};
use alloy_primitives::{address, keccak256, Address, Bytes, TxKind, B256, B64, U256};
use alloy_primitives::{address, keccak256, Address, Bytes, TxKind, B256, U256};
use kona_mpt::{ordered_trie_with_encoder, TrieDB, TrieDBError, TrieHinter, TrieProvider};
use op_alloy_consensus::{OpReceiptEnvelope, OpTxEnvelope};
use op_alloy_genesis::RollupConfig;
Expand Down Expand Up @@ -43,7 +43,10 @@ mod canyon;
use canyon::ensure_create2_deployer_canyon;

mod util;
use util::{logs_bloom, receipt_envelope_from_parts};
use util::{
decode_holocene_eip_1559_params, encode_holocene_eip_1559_params, logs_bloom,
receipt_envelope_from_parts,
};

/// The block executor for the L2 client program. Operates off of a [TrieDB] backed [State],
/// allowing for stateless block execution of OP Stack blocks.
Expand Down Expand Up @@ -108,13 +111,15 @@ where
let block_number = initialized_block_env.number.to::<u64>();
let base_fee = initialized_block_env.basefee.to::<u128>();
let gas_limit = payload.gas_limit.ok_or(ExecutorError::MissingGasLimit)?;
let transactions =
payload.transactions.as_ref().ok_or(ExecutorError::MissingTransactions)?;

info!(
target: "client_executor",
"Executing block # {block_number} | Gas limit: {gas_limit} | Tx count: {tx_len}",
block_number = block_number,
gas_limit = gas_limit,
tx_len = payload.transactions.as_ref().map(|txs| txs.len()).unwrap_or_default(),
tx_len = transactions.len(),
);

let mut state =
Expand All @@ -138,8 +143,7 @@ where
)?;

let mut cumulative_gas_used = 0u64;
let mut receipts: Vec<OpReceiptEnvelope> =
Vec::with_capacity(payload.transactions.as_ref().map(|t| t.len()).unwrap_or_default());
let mut receipts: Vec<OpReceiptEnvelope> = Vec::with_capacity(transactions.len());
let is_regolith = self.config.is_regolith_active(payload.payload_attributes.timestamp);

// Construct the block-scoped EVM with the given configuration.
Expand All @@ -162,18 +166,15 @@ where
};

// Execute the transactions in the payload.
let transactions = if let Some(ref txs) = payload.transactions {
txs.iter()
.map(|raw_tx| {
let tx = OpTxEnvelope::decode_2718(&mut raw_tx.as_ref())
.map_err(ExecutorError::RLPError)?;
Ok((tx, raw_tx.as_ref()))
})
.collect::<ExecutorResult<Vec<_>>>()?
} else {
Vec::new()
};
for (transaction, raw_transaction) in transactions {
let decoded_txs = transactions
.iter()
.map(|raw_tx| {
let tx = OpTxEnvelope::decode_2718(&mut raw_tx.as_ref())
.map_err(ExecutorError::RLPError)?;
Ok((tx, raw_tx.as_ref()))
})
.collect::<ExecutorResult<Vec<_>>>()?;
for (transaction, raw_transaction) in decoded_txs {
// The sum of the transaction’s gas limit, Tg, and the gas utilized in this block prior,
// must be no greater than the block’s gasLimit.
let block_available_gas = (gas_limit - cumulative_gas_used) as u128;
Expand Down Expand Up @@ -261,8 +262,7 @@ where
// Recompute the header roots.
let state_root = state.database.state_root(&bundle)?;

let transactions_root =
Self::compute_transactions_root(payload.transactions.unwrap_or_default().as_slice());
let transactions_root = Self::compute_transactions_root(transactions.as_slice());
let receipts_root = Self::compute_receipts_root(
&receipts,
self.config,
Expand Down Expand Up @@ -303,23 +303,14 @@ where
.unwrap_or_default();

// At holocene activation, the base fee parameters from the payload are placed
// into the Header's `nonce` field. Prior to Holocene, the `nonce` field should
// be set to 0.
// into the Header's `extra_data` field.
//
// If the payload's `eip_1559_params` are equal to `0`, then the header's `nonce`
// If the payload's `eip_1559_params` are equal to `0`, then the header's `extraData`
// field is set to the encoded canyon base fee parameters.
let encoded_base_fee_params = self
.config
.is_holocene_active(payload.payload_attributes.timestamp)
.then(|| {
let payload_params =
payload.eip_1559_params.ok_or(ExecutorError::MissingEIP1559Params)?;

let params = (payload_params == B64::ZERO)
.then(|| encode_canyon_base_fee_params(self.config))
.unwrap_or(payload_params);
Ok::<_, ExecutorError>(params)
})
.then(|| encode_holocene_eip_1559_params(self.config, &payload))
.transpose()?
.unwrap_or_default();

Expand All @@ -340,13 +331,12 @@ where
gas_used: cumulative_gas_used,
timestamp: payload.payload_attributes.timestamp,
mix_hash: payload.payload_attributes.prev_randao,
nonce: encoded_base_fee_params,
nonce: Default::default(),
base_fee_per_gas: base_fee.try_into().ok(),
blob_gas_used,
excess_blob_gas: excess_blob_gas.and_then(|x| x.try_into().ok()),
parent_beacon_block_root: payload.payload_attributes.parent_beacon_block_root,
// Provide no extra data on OP Stack chains
extra_data: Bytes::default(),
extra_data: encoded_base_fee_params,
}
.seal_slow();

Expand Down Expand Up @@ -432,7 +422,9 @@ where
/// ## Returns
/// The active [SpecId] for the executor.
fn revm_spec_id(&self, timestamp: u64) -> SpecId {
if self.config.is_fjord_active(timestamp) {
if self.config.is_holocene_active(timestamp) {
SpecId::HOLOCENE
} else if self.config.is_fjord_active(timestamp) {
SpecId::FJORD
} else if self.config.is_ecotone_active(timestamp) {
SpecId::ECOTONE
Expand Down Expand Up @@ -558,22 +550,12 @@ where
let base_fee_params =
if config.is_holocene_active(payload_attrs.payload_attributes.timestamp) {
// After Holocene activation, the base fee parameters are stored in the
// `nonce` field of the parent header. If the `nonce` field is not zero,
// then the base fee parameters are extracted from the `nonce` field.
// Otherwise, the canyon base fee parameters are used for the current block.
(parent_header.nonce != B64::ZERO)
.then(|| {
// SAFETY: The `nonce` field is always 8 bytes, and the 4-byte segments are
// guaranteed to be valid `u32` values.
let denominator: u32 =
u32::from_be_bytes(parent_header.nonce[0..4].try_into().unwrap());
let elasticity: u32 =
u32::from_be_bytes(parent_header.nonce[4..8].try_into().unwrap());
BaseFeeParams {
max_change_denominator: denominator as u128,
elasticity_multiplier: elasticity as u128,
}
})
// `extraData` field of the parent header. If Holocene wasn't active in the
// parent block, the default base fee parameters are used.
config
.is_holocene_active(parent_header.timestamp)
.then(|| decode_holocene_eip_1559_params(parent_header))
.transpose()?
.unwrap_or(config.canyon_base_fee_params)
} else if config.is_canyon_active(payload_attrs.payload_attributes.timestamp) {
// If the payload attribute timestamp is past canyon activation,
Expand Down Expand Up @@ -705,22 +687,10 @@ where
}
}

/// Encodes the canyon base fee parameters, per Holocene spec.
///
/// <https://specs.optimism.io/protocol/holocene/exec-engine.html#eip1559params-encoding>
fn encode_canyon_base_fee_params(config: &RollupConfig) -> B64 {
let params = config.canyon_base_fee_params;

let mut buf = B64::ZERO;
buf[0..4].copy_from_slice(&(params.max_change_denominator as u32).to_be_bytes());
buf[4..8].copy_from_slice(&(params.elasticity_multiplier as u32).to_be_bytes());
buf
}

#[cfg(test)]
mod test {
use super::*;
use alloy_primitives::{address, b256, b64, hex};
use alloy_primitives::{address, b256, hex};
use alloy_rlp::Decodable;
use alloy_rpc_types_engine::PayloadAttributes;
use anyhow::{anyhow, Result};
Expand Down Expand Up @@ -774,18 +744,6 @@ mod test {
}
}

#[test]
fn test_encode_canyon_1559_params() {
let cfg = RollupConfig {
canyon_base_fee_params: BaseFeeParams {
max_change_denominator: 32,
elasticity_multiplier: 64,
},
..Default::default()
};
assert_eq!(encode_canyon_base_fee_params(&cfg), b64!("0000002000000040"));
}

#[test]
fn test_l2_block_executor_small_block() {
// Static for the execution of block #120794432 on OP mainnet.
Expand Down
Loading

0 comments on commit 17c6c15

Please sign in to comment.