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

feat: chain spec #14

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
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
29 changes: 27 additions & 2 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,8 @@ jobs:
RUST_BACKTRACE: full
RUST_LOG: info


test-e2e:
name: Test
name: Test e2es
runs-on: ["runs-on", "runner=32cpu-linux-x64", "run-id=${{ github.run_id }}"]
env:
CARGO_NET_GIT_FETCH_WITH_CLI: "true"
Expand All @@ -70,6 +69,32 @@ jobs:
run: |
SP1_DEV=1 RUST_LOG=info cargo test -p sp1-cc-host-executor --release -- --nocapture

test-uniswap-forge:
name: Test uniswap forge
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Run Forge build
run: |
cd examples/uniswap/contracts
forge --version
forge build --sizes
id: build

- name: Run Forge tests
run: |
cd contracts
forge test -vvv
id: test




1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ rsp-mpt = { git = "https://github.com/succinctlabs/rsp" , rev = "3647076"}
# reth
reth-primitives = { git = "https://github.com/sp1-patches/reth", tag = "rsp-20240830", default-features = false, features = [
"alloy-compat",
"optimism",
"std",
] }
reth-codecs = { git = "https://github.com/sp1-patches/reth", tag = "rsp-20240830", default-features = false }
Expand All @@ -75,10 +74,8 @@ reth-ethereum-consensus = { git = "https://github.com/sp1-patches/reth", tag = "

# revm
revm = { version = "14.0.0", features = [
"optimism",
"std",
"serde",
"kzg-rs",
], default-features = false }
revm-primitives = { version = "9.0.0", features = [
"std",
Expand All @@ -89,7 +86,6 @@ revm-primitives = { version = "9.0.0", features = [
alloy-primitives = "0.8"
alloy-provider = { version = "0.3", default-features = false, features = [
"reqwest",
"reqwest-rustls-tls",
] }
alloy-rpc-types = { version = "0.3", default-features = false, features = [
"eth",
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,12 @@ Then, from the root directory of the repository, run

where `[example]` is one of the following
* `uniswap`
* Fetches the price of the UNI / WETH pair on Uniswap V3.
* Outputs a file called [plonk-fixture.json](examples/uniswap/contracts/src/fixtures/plonk-fixture.json), which contains everything you need to verify the proof on chain.
* To see an example of on-chain verification, take a look at the [contracts](./examples/uniswap/contracts/) directory.
* On chain verification requires generating a plonk or groth16 proof, which requires significant computational resources. We recommend using the [SP1 Prover network](https://docs.succinct.xyz/generating-proofs/prover-network.html).
* Fetches the price of the UNI / WETH pair on Uniswap V3. By default, this does not generate a proof.
* Running `RUST_LOG=info cargo run --bin [example] --release -- --prove` will generate a plonk proof. This requires
significant computational resources, so we recommend using the [SP1 Prover network]
(https://docs.succinct.xyz/generating-proofs/prover-network.html).
* Outputs a file called [plonk-fixture.json](examples/uniswap/contracts/src/fixtures/plonk-fixture.json), which contains everything you need to verify the proof on chain.
* To see an example of on-chain verification, take a look at the [contracts](./examples/uniswap/contracts/) directory.
* `multiplexer`
* Calls a contract that fetches the prices of many different collateral assets.
* The source code of this contract is found [here](./examples/multiplexer/ZkOracleHelper.sol).
Expand Down
21 changes: 16 additions & 5 deletions crates/client-executor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod io;
use alloy_sol_types::{sol, SolCall};
use eyre::OptionExt;
use io::EVMStateSketch;
use reth_chainspec::ChainSpec;
use reth_evm::ConfigureEvmEnv;
use reth_evm_ethereum::EthEvmConfig;
use reth_primitives::Header;
Expand All @@ -10,6 +11,7 @@ use revm_primitives::{Address, BlockEnv, Bytes, CfgEnvWithHandlerCfg, SpecId, Tx
use rsp_client_executor::io::WitnessInput;
use rsp_witness_db::WitnessDb;

pub use rsp_primitives::chain_spec::mainnet;
/// Input to a contract call.
#[derive(Debug, Clone)]
pub struct ContractInput<C: SolCall> {
Expand All @@ -20,6 +22,9 @@ pub struct ContractInput<C: SolCall> {
/// The calldata to pass to the contract.
pub calldata: C,
}
pub fn sepolia() -> ChainSpec {
(*reth_chainspec::SEPOLIA.clone()).clone()
}

sol! {
/// Public values of a contract call.
Expand Down Expand Up @@ -57,13 +62,19 @@ pub struct ClientExecutor {
pub witness_db: WitnessDb,
/// The block header.
pub header: Header,
/// The chain spec.
pub chain_spec: ChainSpec,
}

impl ClientExecutor {
/// Instantiates a new [`ClientExecutor`]
pub fn new(state_sketch: EVMStateSketch) -> eyre::Result<Self> {
pub fn new(state_sketch: EVMStateSketch, chain_spec: ChainSpec) -> eyre::Result<Self> {
// let header = state_sketch.header.clone();
Ok(Self { witness_db: state_sketch.witness_db().unwrap(), header: state_sketch.header })
Ok(Self {
witness_db: state_sketch.witness_db().unwrap(),
header: state_sketch.header,
chain_spec,
})
}

/// Executes the smart contract call with the given [`ContractInput`] in SP1.
Expand All @@ -74,20 +85,20 @@ impl ClientExecutor {
call: ContractInput<C>,
) -> eyre::Result<ContractPublicValues> {
let cache_db = CacheDB::new(&self.witness_db);
let mut evm = new_evm(cache_db, &self.header, U256::ZERO, &call);
let mut evm = new_evm(cache_db, &self.header, U256::ZERO, &call, &self.chain_spec);
let tx_output = evm.transact()?;
let tx_output_bytes = tx_output.result.output().ok_or_eyre("Error decoding result")?;
Ok(ContractPublicValues::new::<C>(call, tx_output_bytes.clone(), self.header.hash_slow()))
}
}

/// TODO Add support for other chains besides Ethereum Mainnet.
/// Instantiates a new EVM, which is ready to run `call`.
pub fn new_evm<'a, D, C>(
db: D,
header: &Header,
total_difficulty: U256,
call: &ContractInput<C>,
chain_spec: &ChainSpec,
) -> Evm<'a, (), D>
where
D: Database,
Expand All @@ -99,7 +110,7 @@ where
EthEvmConfig::default().fill_cfg_and_block_env(
&mut cfg_env,
&mut block_env,
&rsp_primitives::chain_spec::mainnet(),
chain_spec,
header,
total_difficulty,
);
Expand Down
13 changes: 10 additions & 3 deletions crates/host-executor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use alloy_rpc_types::BlockNumberOrTag;
use alloy_sol_types::SolCall;
use alloy_transport::Transport;
use eyre::{eyre, OptionExt};
use reth_chainspec::ChainSpec;
use reth_primitives::{Block, Header};
use revm::db::CacheDB;
use revm_primitives::{B256, U256};
Expand All @@ -29,25 +30,31 @@ pub struct HostExecutor<T: Transport + Clone, P: Provider<T, AnyNetwork> + Clone
pub rpc_db: RpcDb<T, P>,
/// The provider used to fetch data.
pub provider: P,
/// The chain spec.
pub chain_spec: ChainSpec,
}

impl<T: Transport + Clone, P: Provider<T, AnyNetwork> + Clone> HostExecutor<T, P> {
/// Create a new [`HostExecutor`] with a specific [`Provider`] and [`BlockNumberOrTag`].
pub async fn new(provider: P, block_number: BlockNumberOrTag) -> eyre::Result<Self> {
pub async fn new(
provider: P,
block_number: BlockNumberOrTag,
chain_spec: ChainSpec,
) -> eyre::Result<Self> {
let block = provider
.get_block_by_number(block_number, true)
.await?
.map(|block| Block::try_from(block.inner))
.ok_or(eyre!("couldn't fetch block: {}", block_number))??;

let rpc_db = RpcDb::new(provider.clone(), block.header.number);
Ok(Self { header: block.header, rpc_db, provider })
Ok(Self { header: block.header, rpc_db, provider, chain_spec })
}

/// Executes the smart contract call with the given [`ContractInput`].
pub async fn execute<C: SolCall>(&mut self, call: ContractInput<C>) -> eyre::Result<C::Return> {
let cache_db = CacheDB::new(&self.rpc_db);
let mut evm = new_evm(cache_db, &self.header, U256::ZERO, &call);
let mut evm = new_evm(cache_db, &self.header, U256::ZERO, &call, &self.chain_spec);
let output = evm.transact()?;
let output_bytes = output.result.output().ok_or_eyre("Error getting result")?;

Expand Down
7 changes: 5 additions & 2 deletions crates/host-executor/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ async fn test_e2e<C: SolCall + Clone>(
// Load environment variables.
dotenv::dotenv().ok();

let mainnet = rsp_primitives::chain_spec::mainnet();

// Which block transactions are executed on.
let block_number = BlockNumberOrTag::Latest;

Expand All @@ -97,14 +99,15 @@ async fn test_e2e<C: SolCall + Clone>(
// Use `RPC_URL` to get all of the necessary state for the smart contract call.
let rpc_url = std::env::var("ETH_RPC_URL").unwrap_or_else(|_| panic!("Missing RPC_URL"));
let provider = ReqwestProvider::new_http(Url::parse(&rpc_url)?);
let mut host_executor = HostExecutor::new(provider.clone(), block_number).await?;
let mut host_executor =
HostExecutor::new(provider.clone(), block_number, mainnet.clone()).await?;

let _contract_output = host_executor.execute(contract_input.clone()).await?;

// Now that we've executed all of the calls, get the `EVMStateSketch` from the host executor.
let state_sketch = host_executor.finalize().await?;

let client_executor = ClientExecutor::new(state_sketch)?;
let client_executor = ClientExecutor::new(state_sketch, mainnet.clone())?;

let public_values = client_executor.execute(contract_input)?;

Expand Down
8 changes: 3 additions & 5 deletions examples/multiplexer/client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ sp1_zkvm::entrypoint!(main);

use alloy_primitives::{address, Address};
use alloy_sol_macro::sol;
use alloy_sol_types::{SolCall, SolValue};
use alloy_sol_types::SolValue;
use bincode;
use sp1_cc_client_executor::{
io::EVMStateSketch, ClientExecutor, ContractInput, ContractPublicValues,
};
use sp1_cc_client_executor::{io::EVMStateSketch, mainnet, ClientExecutor, ContractInput};

sol! {
/// Interface to the multiplexer contract. It gets the prices of many tokens, including
Expand Down Expand Up @@ -47,7 +45,7 @@ pub fn main() {

// Initialize the client executor with the state sketch.
// This step also validates all of the storage against the provided state root.
let executor = ClientExecutor::new(state_sketch).unwrap();
let executor = ClientExecutor::new(state_sketch, mainnet()).unwrap();

// Execute the getRates call using the client executor.
let calldata = IOracleHelper::getRatesCall { collaterals: COLLATERALS.to_vec() };
Expand Down
4 changes: 2 additions & 2 deletions examples/multiplexer/host/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use alloy_provider::ReqwestProvider;
use alloy_rpc_types::BlockNumberOrTag;
use alloy_sol_macro::sol;
use alloy_sol_types::{SolCall, SolValue};
use sp1_cc_client_executor::{ContractInput, ContractPublicValues};
use sp1_cc_client_executor::{mainnet, ContractInput, ContractPublicValues};
use sp1_cc_host_executor::HostExecutor;
use sp1_sdk::{utils, ProverClient, SP1Stdin};
use url::Url;
Expand Down Expand Up @@ -56,7 +56,7 @@ async fn main() -> eyre::Result<()> {
let rpc_url =
std::env::var("ETH_RPC_URL").unwrap_or_else(|_| panic!("Missing ETH_RPC_URL in env"));
let provider = ReqwestProvider::new_http(Url::parse(&rpc_url)?);
let mut host_executor = HostExecutor::new(provider.clone(), block_number).await?;
let mut host_executor = HostExecutor::new(provider.clone(), block_number, mainnet()).await?;

// Keep track of the block hash. Later, the client's execution will be validated against this.
let block_hash = host_executor.header.hash_slow();
Expand Down
6 changes: 3 additions & 3 deletions examples/uniswap/client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ sp1_zkvm::entrypoint!(main);

use alloy_primitives::{address, Address};
use alloy_sol_macro::sol;
use alloy_sol_types::{SolCall, SolValue};
use alloy_sol_types::SolValue;
use bincode;
use sp1_cc_client_executor::{io::EVMStateSketch, ClientExecutor, ContractInput};
use sp1_cc_client_executor::{io::EVMStateSketch, mainnet, ClientExecutor, ContractInput};
sol! {
/// Simplified interface of the IUniswapV3PoolState interface.
interface IUniswapV3PoolState {
Expand All @@ -28,7 +28,7 @@ pub fn main() {

// Initialize the client executor with the state sketch.
// This step also validates all of the storage against the provided state root.
let executor = ClientExecutor::new(state_sketch).unwrap();
let executor = ClientExecutor::new(state_sketch, mainnet()).unwrap();

// Execute the slot0 call using the client executor.
let slot0_call = IUniswapV3PoolState::slot0Call {};
Expand Down
1 change: 1 addition & 0 deletions examples/uniswap/host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ eyre.workspace = true
bincode.workspace = true
serde.workspace = true
serde_json.workspace = true
clap = { version = "4.0", features = ["derive", "env"] }

# sp1
sp1-sdk = { git = "https://github.com/succinctlabs/sp1", branch = "yuwen/fs-test"}
Expand Down
21 changes: 19 additions & 2 deletions examples/uniswap/host/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ use alloy_provider::ReqwestProvider;
use alloy_rpc_types::BlockNumberOrTag;
use alloy_sol_macro::sol;
use alloy_sol_types::{SolCall, SolValue};
use clap::Parser;
use serde::{Deserialize, Serialize};
use sp1_cc_client_executor::{ContractInput, ContractPublicValues};
use sp1_cc_client_executor::{mainnet, ContractInput, ContractPublicValues};
use sp1_cc_host_executor::HostExecutor;
use sp1_sdk::{utils, HashableKey, ProverClient, SP1ProofWithPublicValues, SP1Stdin};
use url::Url;
Expand Down Expand Up @@ -38,6 +39,14 @@ struct SP1CCProofFixture {
proof: String,
}

/// The arguments for the command.
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
#[clap(long, default_value = "false")]
prove: bool,
}

/// Generate a `SP1CCProofFixture`, and save it as a json file.
///
/// This is useful for verifying the proof of contract call execution on chain.
Expand All @@ -62,6 +71,9 @@ async fn main() -> eyre::Result<()> {
// Setup logging.
utils::setup_logger();

// Parse the command line arguments.
let args = Args::parse();

// Which block transactions are executed on.
let block_number = BlockNumberOrTag::Number(20600000);

Expand All @@ -71,7 +83,7 @@ async fn main() -> eyre::Result<()> {
let rpc_url =
std::env::var("ETH_RPC_URL").unwrap_or_else(|_| panic!("Missing ETH_RPC_URL in env"));
let provider = ReqwestProvider::new_http(Url::parse(&rpc_url)?);
let mut host_executor = HostExecutor::new(provider.clone(), block_number).await?;
let mut host_executor = HostExecutor::new(provider.clone(), block_number, mainnet()).await?;

// Keep track of the block hash. Later, validate the client's execution against this.
let block_hash = host_executor.header.hash_slow();
Expand Down Expand Up @@ -102,6 +114,11 @@ async fn main() -> eyre::Result<()> {
let (_, report) = client.execute(ELF, stdin.clone()).run().unwrap();
println!("executed program with {} cycles", report.total_instruction_count());

// If the prove flag is not set, we return here.
if !args.prove {
return Ok(());
}

// Generate the proof for the given program and input.
let (pk, vk) = client.setup(ELF);
let proof = client.prove(&pk, stdin).plonk().run().unwrap();
Expand Down
4 changes: 2 additions & 2 deletions examples/verify-quorum/client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use alloy_primitives::{address, Address, Bytes, B256};
use alloy_sol_macro::sol;
use alloy_sol_types::SolValue;
use bincode;
use sp1_cc_client_executor::{io::EVMStateSketch, ClientExecutor, ContractInput};
use sp1_cc_client_executor::{io::EVMStateSketch, sepolia, ClientExecutor, ContractInput};

sol! {
/// Part of the SimpleStaking interface
Expand Down Expand Up @@ -34,7 +34,7 @@ pub fn main() {

// Initialize the client executor with the state sketch.
// This step also validates all of the storage against the provided state root.
let executor = ClientExecutor::new(state_sketch).unwrap();
let executor = ClientExecutor::new(state_sketch, sepolia()).unwrap();

// Set up the call to `verifySigned`.
let verify_signed_call = ContractInput {
Expand Down
Loading