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

WEB3-315: feat: Add EVM Event support to Steel #409

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ risc0-binfmt = { git = "https://github.com/risc0/risc0", branch = "main", defaul

# Alloy guest dependencies
alloy-consensus = { version = "0.9" }
alloy-eips = { version = "0.9" }
alloy-rlp = { version = "0.3.8" }
alloy-primitives = { version = "0.8.16" }
alloy-rpc-types = { version = "0.9" }
alloy-sol-types = { version = "0.8.16" }

# OP Steel
Expand Down
1 change: 1 addition & 0 deletions crates/op-steel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ risc0-op-steel = { workspace = true, features = ["host"] }
[features]
default = []
host = ["dep:alloy", "dep:tokio", "dep:url", "risc0-steel/host", "alloy-primitives/rand"]
unstable-event = []
10 changes: 10 additions & 0 deletions crates/op-steel/src/optimism/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ impl EvmBlockHeader for OpBlockHeader {
fn state_root(&self) -> &B256 {
&self.0.inner().state_root
}
#[cfg(feature = "unstable-event")]
#[inline]
fn receipts_root(&self) -> &B256 {
&self.0.inner().receipts_root
}
#[cfg(feature = "unstable-event")]
#[inline]
fn logs_bloom(&self) -> &alloy_primitives::Bloom {
&self.0.inner().logs_bloom
}

#[inline]
fn fill_block_env(&self, blk_env: &mut BlockEnv) {
Expand Down
7 changes: 5 additions & 2 deletions crates/steel/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

<!-- TODO: Add link when release is finished -->
## [1.3.0]
### ⚡️ Features

- Introduce the capability to query Ethereum events. The new `Event` allows to query events of a specific type in Steel. Its usage is very similar to the existing `Contract`, during the preflight step and in the guest. This functionality is currently marked unstable and must be enabled using the `unstable-event` feature.

## [1.3.0](https://github.com/risc0/risc0-ethereum/releases/tag/v1.3.0)

### ⚡️ Features

Expand Down
3 changes: 3 additions & 0 deletions crates/steel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
alloy = { workspace = true, optional = true, features = ["full"] }
alloy-consensus = { workspace = true }
alloy-eips = { workspace = true }
alloy-primitives = { workspace = true, features = ["rlp", "serde"] }
alloy-rlp = { workspace = true }
alloy-rpc-types = { workspace = true }
alloy-sol-types = { workspace = true }
alloy-trie = { workspace = true, features = ["serde"] }
anyhow = { workspace = true }
Expand Down Expand Up @@ -50,5 +52,6 @@ host = [
"dep:tokio",
"dep:url",
]
unstable-event = []
unstable-history = []
unstable-verifier = []
46 changes: 41 additions & 5 deletions crates/steel/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@
// limitations under the License.

use crate::{
config::ChainSpec, state::StateDb, BlockHeaderCommit, Commitment, CommitmentVersion,
EvmBlockHeader, EvmEnv, GuestEvmEnv, MerkleTrie,
config::ChainSpec, serde::Eip2718Wrapper, state::StateDb, BlockHeaderCommit, Commitment,
CommitmentVersion, EvmBlockHeader, EvmEnv, GuestEvmEnv, MerkleTrie,
};
use ::serde::{Deserialize, Serialize};
use alloy_consensus::ReceiptEnvelope;
use alloy_primitives::{map::HashMap, Bytes, Sealed, B256};

/// Input committing to the corresponding execution block hash.
Expand All @@ -27,6 +28,7 @@ pub struct BlockInput<H> {
storage_tries: Vec<MerkleTrie>,
contracts: Vec<Bytes>,
ancestors: Vec<H>,
receipts: Option<Vec<Eip2718Wrapper<ReceiptEnvelope>>>,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The ReceiptEnvelope cannot be serialized using bincode or similar, so it must be RLP encoded. However, since the hash calculation also relies on the RLP encoding, this may actually be a performance improvement.

}

/// Implement [BlockHeaderCommit] for the unit type.
Expand Down Expand Up @@ -72,11 +74,38 @@ impl<H: EvmBlockHeader> BlockInput<H> {
previous_header = ancestor;
}

#[cfg(not(feature = "unstable-event"))]
// there must not be any receipts, if events are not supported
let logs = {
assert!(self.receipts.is_none(), "Receipts not supported");
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
assert!(self.receipts.is_none(), "Receipts not supported");
assert!(self.receipts.is_none(), "Receipts not supported; unstable-event feature not enabled");

None
};
#[cfg(feature = "unstable-event")]
// verify the root hash of the included receipts and extract their logs
let logs = self.receipts.map(|receipts| {
let root = alloy_trie::root::ordered_trie_root_with_encoder(&receipts, |r, out| {
alloy_eips::eip2718::Encodable2718::encode_2718(r, out)
});
assert_eq!(header.receipts_root(), &root, "Receipts root mismatch");

receipts
.into_iter()
.flat_map(|wrapper| match wrapper.into_inner() {
ReceiptEnvelope::Legacy(t) => t.receipt.logs,
ReceiptEnvelope::Eip2930(t) => t.receipt.logs,
ReceiptEnvelope::Eip1559(t) => t.receipt.logs,
ReceiptEnvelope::Eip4844(t) => t.receipt.logs,
ReceiptEnvelope::Eip7702(t) => t.receipt.logs,
})
.collect()
});

let db = StateDb::new(
self.state_trie,
self.storage_tries,
self.contracts,
block_hashes,
logs,
);
let commit = Commitment::new(
CommitmentVersion::Block as u16,
Expand All @@ -91,19 +120,19 @@ impl<H: EvmBlockHeader> BlockInput<H> {

#[cfg(feature = "host")]
pub mod host {
use std::fmt::Display;

use super::BlockInput;
use crate::{
host::db::{AlloyDb, ProofDb, ProviderDb},
serde::Eip2718Wrapper,
EvmBlockHeader,
};
use alloy::{network::Network, providers::Provider, transports::Transport};
use alloy_primitives::Sealed;
use anyhow::{anyhow, ensure};
use log::debug;
use std::fmt::Display;

impl<H: EvmBlockHeader> BlockInput<H> {
impl<H> BlockInput<H> {
/// Creates the `BlockInput` containing the necessary EVM state that can be verified against
/// the block hash.
pub(crate) async fn from_proof_db<T, N, P>(
Expand Down Expand Up @@ -137,6 +166,11 @@ pub mod host {
ancestors.push(header);
}

let receipts = db.receipt_proof().await?;
// wrap the receipts so that they can be serialized
let receipts =
receipts.map(|receipts| receipts.into_iter().map(Eip2718Wrapper::new).collect());

debug!("state size: {}", state_trie.size());
debug!("storage tries: {}", storage_tries.len());
debug!(
Expand All @@ -145,13 +179,15 @@ pub mod host {
);
debug!("contracts: {}", contracts.len());
debug!("ancestor blocks: {}", ancestors.len());
debug!("receipts: {:?}", receipts.as_ref().map(Vec::len));

let input = BlockInput {
header: header.into_inner(),
state_trie,
storage_tries,
contracts,
ancestors,
receipts,
};

Ok(input)
Expand Down
6 changes: 3 additions & 3 deletions crates/steel/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ use revm::{
///
/// ### Usage
/// - **Preflight calls on the Host:** To prepare calls on the host environment and build the
/// necessary proof, use [Contract::preflight][Contract]. The environment can be initialized using the
/// [EthEvmEnv::builder] or [EvmEnv::builder].
/// necessary proof, use [Contract::preflight][Contract]. The environment can be initialized using
/// the [EthEvmEnv::builder] or [EvmEnv::builder].
/// - **Calls in the Guest:** To initialize the contract in the guest environment, use
/// [Contract::new]. The environment should be constructed using [EvmInput::into_env].
///
Expand Down Expand Up @@ -284,7 +284,7 @@ where
/// required.
pub fn try_call(self) -> Result<S::Return, String> {
let mut evm = new_evm::<_, H>(
WrapStateDb::new(self.env.db()),
WrapStateDb::new(self.env.db(), &self.env.header),
self.env.cfg_env.clone(),
self.env.header.inner(),
);
Expand Down
10 changes: 10 additions & 0 deletions crates/steel/src/ethereum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ impl EvmBlockHeader for EthBlockHeader {
fn state_root(&self) -> &B256 {
&self.inner().state_root
}
#[cfg(feature = "unstable-event")]
#[inline]
fn receipts_root(&self) -> &B256 {
&self.inner().receipts_root
}
#[cfg(feature = "unstable-event")]
#[inline]
fn logs_bloom(&self) -> &alloy_primitives::Bloom {
&self.inner().logs_bloom
}

#[inline]
fn fill_block_env(&self, blk_env: &mut BlockEnv) {
Expand Down
Loading
Loading