Skip to content

Commit

Permalink
feat: verifiable-api (#505)
Browse files Browse the repository at this point in the history
* feat: init verifiable-api crate

* handle empty account or storage value in proof verification

* fixup

* impl get_filter_logs handler

* impl more methods

* impl get_logs, lots of refactor

* single account endpoint

* remove redundant Get from response types

* divide into server, client, types crates

* new common crate to avoid cyclic deps

* verifiable_api in config and usage

* impl create_access_list

* add wasm binding for eth_getStorageAt

* fix getAccount storage, getLogs

* make it work with opstack

* fix receipt proof logic

* replace MAX_SUPPORTED_LOGS_NUMBER with MAX_SUPPORTED_BLOCKS_TO_PROVE_FOR_LOGS

* fix clippy warns

* some cleanup & add usage in README

* update cli args & README.md

* move logic from handlers to ApiService

* impl all used methods in verifiable api

* make new traits wasm compat

* add get_block and make code optional

* use camelCase in paths for consistency

* handle error in VerifiableApiClient

* refactor with ExecutionInner & dyn dispatch

* address review comments

* refactor with new ExecutionSpec trait

* tests for ApiService & ExecutionInnerClient

* refactor into VerifiableApiServer struct

* also test verifiable-api in rpc_equivalance tests

* fix clippy, update README

* support transaction_detail_flag in get_block

* fix code_hash verification

* impl create_access_list in Evm

* expose eth_createAccessList, block param in eth_estimateGas

* expose eth_getProof in RPC

* address review comments

* doc-strings, openapi.yaml and handler for same

* fix err handling in Evm.create_access_list

* validateTx flag in createExtendedAccessList
  • Loading branch information
eshaan7 authored Mar 7, 2025
1 parent e52d85a commit 5dc4fde
Show file tree
Hide file tree
Showing 99 changed files with 17,544 additions and 1,595 deletions.
1,754 changes: 1,106 additions & 648 deletions Cargo.lock

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ exclude = ["benches"]
[workspace]
members = [
"cli",
"common",
"core",
"ethereum",
"ethereum/consensus-core",
"opstack",
"helios-ts",
"tests/test-utils", # ToDo(@eshaan7): find a better way to include this
"verifiable-api/client",
"verifiable-api/server",
"verifiable-api/types",
]

default-members = ["cli"]
Expand Down Expand Up @@ -83,14 +88,18 @@ typenum = "1.17.0"
######################################

[dependencies]
helios-common = { path = "./common" }
helios-core = { path = "./core" }
helios-ethereum = { path = "./ethereum" }
helios-opstack = { path = "./opstack" }

[dev-dependencies]
tokio = { version = "1", features = ["full"] }
tokio.workspace = true
futures.workspace = true
url.workspace = true
serde.workspace = true
dotenv = "0.15.0"
serde = { version = "1.0.154", features = ["derive"] }
helios-verifiable-api-server = { path = "verifiable-api/server" }

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
alloy = { version = "0.9.1", features = ["full"] }
Expand Down
6 changes: 4 additions & 2 deletions benches/get_balance.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::str::FromStr;

use alloy::primitives::Address;
use criterion::{criterion_group, criterion_main, Criterion};
use helios_core::types::BlockTag;
use std::str::FromStr;

use helios_common::types::BlockTag;

mod harness;

Expand Down
6 changes: 4 additions & 2 deletions benches/get_code.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::str::FromStr;

use alloy::primitives::Address;
use criterion::{criterion_group, criterion_main, Criterion};
use helios_core::types::BlockTag;
use std::str::FromStr;

use helios_common::types::BlockTag;

mod harness;

Expand Down
6 changes: 4 additions & 2 deletions benches/harness.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
#![allow(dead_code)]
use std::{path::PathBuf, str::FromStr};

use alloy::primitives::{Address, B256, U256};
use helios_core::types::BlockTag;

use helios_common::types::BlockTag;
use helios_ethereum::{
config::{checkpoints, networks},
database::FileDB,
EthereumClient, EthereumClientBuilder,
};
use std::{path::PathBuf, str::FromStr};

/// Fetches the latest mainnet checkpoint from the fallback service.
///
Expand Down
3 changes: 2 additions & 1 deletion benches/sync.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use alloy::primitives::Address;
use criterion::{criterion_group, criterion_main, Criterion};
use helios_core::types::BlockTag;

use helios_common::types::BlockTag;

mod harness;

Expand Down
2 changes: 2 additions & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ dirs = "5.0.1"
ctrlc = "3.2.3"
url = "2.5.0"

# self crates
helios-common = { path = "../common" }
helios-core = { path = "../core" }
helios-ethereum = { path = "../ethereum" }
helios-opstack = { path = "../opstack" }
20 changes: 15 additions & 5 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,18 @@ use eyre::Result;
use figment::providers::Serialized;
use figment::value::Value;
use futures::executor::block_on;
use tracing::{error, info};
use tracing_subscriber::filter::{EnvFilter, LevelFilter};
use tracing_subscriber::FmtSubscriber;
use url::Url;

use helios_common::network_spec::NetworkSpec;
use helios_core::client::Client;
use helios_core::consensus::Consensus;
use helios_core::network_spec::NetworkSpec;
use helios_ethereum::config::{cli::CliConfig, Config as EthereumConfig};
use helios_ethereum::database::FileDB;
use helios_ethereum::{EthereumClient, EthereumClientBuilder};
use helios_opstack::{config::Config as OpStackConfig, OpStackClient, OpStackClientBuilder};
use tracing::{error, info};
use tracing_subscriber::filter::{EnvFilter, LevelFilter};
use tracing_subscriber::FmtSubscriber;
use url::Url;

#[tokio::main]
async fn main() -> Result<()> {
Expand Down Expand Up @@ -128,6 +129,8 @@ struct EthereumArgs {
checkpoint: Option<B256>,
#[clap(short, long, env, value_parser = parse_url)]
execution_rpc: Option<Url>,
#[clap(long, env, value_parser = parse_url)]
execution_verifiable_api: Option<Url>,
#[clap(short, long, env, value_parser = parse_url)]
consensus_rpc: Option<Url>,
#[clap(short, long, env)]
Expand Down Expand Up @@ -162,6 +165,7 @@ impl EthereumArgs {
CliConfig {
checkpoint: self.checkpoint,
execution_rpc: self.execution_rpc.clone(),
execution_verifiable_api: self.execution_verifiable_api.clone(),
consensus_rpc: self.consensus_rpc.clone(),
data_dir: self
.data_dir
Expand All @@ -186,6 +190,8 @@ struct OpStackArgs {
rpc_port: Option<u16>,
#[clap(short, long, env, value_parser = parse_url)]
execution_rpc: Option<Url>,
#[clap(long, env, value_parser = parse_url)]
execution_verifiable_api: Option<Url>,
#[clap(short, long, env, value_parser = parse_url)]
consensus_rpc: Option<Url>,
#[clap(
Expand Down Expand Up @@ -226,6 +232,10 @@ impl OpStackArgs {
user_dict.insert("execution_rpc", Value::from(rpc.to_string()));
}

if let Some(api) = &self.execution_verifiable_api {
user_dict.insert("execution_verifiable_api", Value::from(api.to_string()));
}

if let Some(rpc) = &self.consensus_rpc {
user_dict.insert("consensus_rpc", Value::from(rpc.to_string()));
}
Expand Down
11 changes: 11 additions & 0 deletions common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "helios-common"
version = "0.1.0"
edition = "2021"

[dependencies]
alloy.workspace = true
serde.workspace = true
serde_json.workspace = true
revm.workspace = true
eyre.workspace = true
16 changes: 16 additions & 0 deletions common/src/execution_mode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#[derive(Clone)]
pub enum ExecutionMode {
Rpc(String),
VerifiableApi(String),
}

impl ExecutionMode {
pub fn from_urls(rpc: Option<String>, verifiable_api: Option<String>) -> Self {
match (rpc, verifiable_api) {
// we prioritize verifiable_api over rpc
(_, Some(verifiable_api)) => Self::VerifiableApi(verifiable_api),
(Some(rpc), None) => Self::Rpc(rpc),
(None, None) => panic!("Must specify either execution_rpc or execution_verifiable_api"),
}
}
}
File renamed without changes.
4 changes: 4 additions & 0 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod execution_mode;
pub mod fork_schedule;
pub mod network_spec;
pub mod types;
File renamed without changes.
126 changes: 126 additions & 0 deletions common/src/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use std::fmt::Display;

use alloy::{
consensus::Account as TrieAccount,
eips::{BlockId, BlockNumberOrTag},
primitives::{Bytes, B256, U256},
rpc::types::EIP1186StorageProof,
};
use eyre::{eyre, Report, Result};
use serde::{de::Error, Deserialize, Serialize};

#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct Account {
pub account: TrieAccount,
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<Bytes>,
pub account_proof: Vec<Bytes>,
pub storage_proof: Vec<EIP1186StorageProof>,
}

impl Account {
/// Retrieve the value at the given storage slot.
pub fn get_storage_value(&self, slot: B256) -> Option<U256> {
self.storage_proof
.iter()
.find_map(|EIP1186StorageProof { key, value, .. }| {
if key.as_b256() == slot {
Some(*value)
} else {
None
}
})
}
}

#[derive(Debug, Clone, Copy)]
pub enum BlockTag {
Latest,
Finalized,
Number(u64),
}

impl From<u64> for BlockTag {
fn from(num: u64) -> Self {
BlockTag::Number(num)
}
}

impl Display for BlockTag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let formatted = match self {
Self::Latest => "latest".to_string(),
Self::Finalized => "finalized".to_string(),
Self::Number(num) => num.to_string(),
};

write!(f, "{formatted}")
}
}

impl<'de> Deserialize<'de> for BlockTag {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let block: String = serde::Deserialize::deserialize(deserializer)?;
let parse_error = D::Error::custom("could not parse block tag");

let block_tag = match block.as_str() {
"latest" => BlockTag::Latest,
"finalized" => BlockTag::Finalized,
_ => match block.strip_prefix("0x") {
Some(hex_block) => {
let num = u64::from_str_radix(hex_block, 16).map_err(|_| parse_error)?;

BlockTag::Number(num)
}
None => {
let num = block.parse().map_err(|_| parse_error)?;

BlockTag::Number(num)
}
},
};

Ok(block_tag)
}
}

impl From<BlockTag> for BlockId {
fn from(block_tag: BlockTag) -> Self {
match block_tag {
BlockTag::Latest => BlockId::latest(),
BlockTag::Finalized => BlockId::finalized(),
BlockTag::Number(num) => BlockId::Number(num.into()),
}
}
}

impl TryFrom<BlockNumberOrTag> for BlockTag {
type Error = Report;

fn try_from(tag: BlockNumberOrTag) -> Result<Self, Self::Error> {
match tag {
BlockNumberOrTag::Number(num) => Ok(BlockTag::Number(num)),
BlockNumberOrTag::Latest => Ok(BlockTag::Latest),
BlockNumberOrTag::Finalized => Ok(BlockTag::Finalized),
other => Err(eyre!("block tag {other} is not supported")),
}
}
}

impl TryFrom<BlockId> for BlockTag {
type Error = Report;

fn try_from(block_id: BlockId) -> Result<Self, Self::Error> {
match block_id {
BlockId::Number(BlockNumberOrTag::Number(num)) => Ok(BlockTag::Number(num)),
BlockId::Number(BlockNumberOrTag::Latest) => Ok(BlockTag::Latest),
BlockId::Number(BlockNumberOrTag::Finalized) => Ok(BlockTag::Finalized),
BlockId::Number(other) => Err(eyre!("block tag {other} is not supported")),
BlockId::Hash(_) => Err(eyre!("block hash is not supported")),
}
}
}
8 changes: 8 additions & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ version = "0.8.3"
edition = "2021"

[dependencies]
# self crates
helios-common = { path = "../common" }
helios-verifiable-api-client = { path = "../verifiable-api/client" }

# execution
alloy.workspace = true
alloy-trie.workspace = true
Expand Down Expand Up @@ -36,3 +40,7 @@ wasmtimer = "0.2.0"

[target.wasm32-unknown-unknown.dependencies]
parking_lot = { version = "0.12.2" }

[dev-dependencies]
helios-test-utils = { path = "../tests/test-utils" }
helios-ethereum = { path = "../ethereum" }
Loading

0 comments on commit 5dc4fde

Please sign in to comment.