Skip to content

Commit

Permalink
WEB3-67: feat: switch Steel to alloy (#183)
Browse files Browse the repository at this point in the history
### ⚡️ Features

- Replace `ethers` dependency completely with `alloy`.
- Make `host` functions `async`.
- Add support to build `EvmEnv` from any `alloy` provider.
- Add more efficient RLP-based serialization for the header.

### 🛠 Fixes

- Store the commitment inside the `EvmEnv`.
- Use `eth_getTransactionCount` and `eth_getBalance` instead of
`eth_getProof` to query basic account information.
- Switch tests from pre-recorded RPC responses to `Anvil`.

### 🚨 Breaking Changes

- `EthEvmEnv::from_rpc` now accepts a `Url` instead of a `&str` for the
HTTP RPC endpoint.
- `EvmEnv::from_provider` now requires an `alloy` provider, and the
block number parameter has been changed to a `BlockNumberOrTag`.
- `EvmEnv::sol_commitment` has been replaced with `EvmEnv::commitment`
(to get a reference), or `EvmEnv::into_commitment` (to consume and
return the commitment).
- `ETH_SEPOLIA_CHAIN_SPEC` and `ETH_MAINNET_CHAIN_SPEC` have been moved
to the `ethereum` module.
- `CachedProvider` has been removed completely. As alternatives, you
can:
- Use `anvil --fork-url https://ethereum-rpc.publicnode.com@20475759` to
create a cached fork for block `20475759`.
- Cache the RPC responses on an HTTP level using
[Tower](https://crates.io/crates/tower) or a caching forward proxy.
- The host functions are now `async` instead of blocking:
```rust
// Create an EVM environment from an RPC endpoint and a block number or tag.
let mut env = EthEvmEnv::from_rpc(args.rpc_url, BlockNumberOrTag::Latest).await?;
//  The `with_chain_spec` method is used to specify the chain configuration.
env = env.with_chain_spec(&ETH_SEPOLIA_CHAIN_SPEC);

// Preflight the call to prepare the input that is required to execute the function in
// the guest without RPC access. It also returns the result of the call.
let mut contract = Contract::preflight(CONTRACT, &mut env);
let returns = contract.call_builder(&CALL).from(CALLER).call().await?;

// Finally, construct the input from the environment.
let input = env.into_input().await?;
```

---------

Co-authored-by: Angelo Capossele <[email protected]>
Co-authored-by: Victor Graf <[email protected]>
  • Loading branch information
3 people authored Aug 7, 2024
1 parent bb0dceb commit b2dcc8e
Show file tree
Hide file tree
Showing 55 changed files with 1,785 additions and 3,336 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,15 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [Linux, macOS]
feature: [default]
device: [cpu]
include:
- os: Linux
feature: default
device: cpu
- os: Linux
feature: cuda
device: nvidia_rtx_a5000
- os: macOS
feature: metal
feature: default
device: apple_m2_pro
env:
FEATURE: ${{ matrix.feature }}
Expand Down
16 changes: 10 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,27 @@ risc0-build = { git = "https://github.com/risc0/risc0", branch = "main", default
risc0-zkp = { git = "https://github.com/risc0/risc0", branch = "main", default-features = false }
risc0-zkvm = { git = "https://github.com/risc0/risc0", branch = "main", default-features = false }

alloy-primitives = { version = "0.7", features = ["serde", "rlp", "std"] }
alloy-rlp = { version = "0.3.7", default-features = false }
alloy-rlp-derive = { version = "0.3.7", default-features = false }
# Alloy guest dependencies
alloy-consensus = { version = "0.2.1" }
alloy-primitives = { version = "0.7", features = ["rlp", "std"] }
alloy-rlp = { version = "0.3.8", default-features = false }
alloy-rlp-derive = { version = "0.3.8", default-features = false }
alloy-sol-types = { version = "0.7" }

# Alloy host dependencies
alloy = { version = "0.2.1", features = ["full"] }
alloy-trie = { version = "0.4.0" }

anyhow = { version = "1.0" }
bincode = { version = "1.3" }
clap = { version = "4.5", features = ["derive", "env"] }
ethers-core = "2.0"
ethers-providers = "2.0"
log = "0.4"
nybbles = { version = "0.2.1", features = ["serde"] }
once_cell = "1.19"
revm = { version = "10.0", default-features = false, features = ["std"] }
serde = "1.0"
serde_json = "1.0"
test-log = "0.2.15"
thiserror = "1.0"
tokio = { version = "1.35" }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
url = { version = "2.5" }
6 changes: 3 additions & 3 deletions contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ repository = { workspace = true }
anyhow = "1.0"

[dependencies]
alloy-sol-types = { workspace = true }
alloy = { workspace = true, features = ["sol-types", "contract"] }
anyhow = { workspace = true }
ethers = { version = "2.0", features = ["rustls", "ws"] }
risc0-zkvm = { workspace = true }

[dev-dependencies]
hex = "0.4"
regex = "1.10"
tokio = { version = "1", features = ["macros", "rt"] }
tokio = { workspace = true, features = ["macros", "rt"] }

[lib]
doctest = false
Expand Down
41 changes: 0 additions & 41 deletions contracts/build.rs

This file was deleted.

20 changes: 13 additions & 7 deletions contracts/src/groth16.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,37 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use alloy_sol_types::SolValue;
use alloy::sol_types::SolValue;
use anyhow::Result;
use risc0_zkvm::{sha::Digestible, Groth16ReceiptVerifierParameters};

/// ABI encoding of the seal.
pub fn abi_encode(seal: Vec<u8>) -> Result<Vec<u8>> {
pub fn abi_encode(seal: impl AsRef<[u8]>) -> Result<Vec<u8>> {
Ok(encode(seal)?.abi_encode())
}

/// encoding of the seal with selector.
pub fn encode(seal: Vec<u8>) -> Result<Vec<u8>> {
/// Encoding of a Groth16 seal by prefixing it with the verifier selector.
///
/// The verifier selector is determined from the first 4 bytes of the hash of the verifier
/// parameters including the Groth16 verification key and the control IDs that commit to the RISC
/// Zero circuits.
///
/// NOTE: Selector value of the current zkVM version is used. If you need to use a selector from a
/// different version of the zkVM, use the [encode_seal] method instead.
pub fn encode(seal: impl AsRef<[u8]>) -> Result<Vec<u8>> {
let verifier_parameters_digest = Groth16ReceiptVerifierParameters::default().digest();
let selector = &verifier_parameters_digest.as_bytes()[..4];
// Create a new vector with the capacity to hold both selector and seal
let mut selector_seal = Vec::with_capacity(selector.len() + seal.len());
let mut selector_seal = Vec::with_capacity(selector.len() + seal.as_ref().len());
selector_seal.extend_from_slice(selector);
selector_seal.extend_from_slice(&seal);
selector_seal.extend_from_slice(seal.as_ref());

Ok(selector_seal)
}

#[cfg(test)]
mod tests {
use anyhow::anyhow;
use ethers::utils::hex;
use regex::Regex;

use super::*;
Expand Down
44 changes: 35 additions & 9 deletions contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,41 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use ethers::prelude::*;

abigen!(
IRiscZeroVerifier,
"$OUT_DIR/IRiscZeroVerifier.sol/IRiscZeroVerifier.json"
);
abigen!(
RiscZeroGroth16Verifier,
"$OUT_DIR/RiscZeroGroth16Verifier.sol/RiscZeroGroth16Verifier.json"
alloy::sol!(
#![sol(rpc, all_derives)]
"src/IRiscZeroVerifier.sol"
);

pub mod groth16;

use anyhow::{bail, Result};
use risc0_zkvm::{sha::Digestible, InnerReceipt};

/// Encode the seal of the given receipt for use with EVM smart contract verifiers.
///
/// Appends the verifier selector, determined from the first 4 bytes of the verifier parameters
/// including the Groth16 verification key and the control IDs that commit to the RISC Zero
/// circuits.
pub fn encode_seal(receipt: &risc0_zkvm::Receipt) -> Result<Vec<u8>> {
let seal = match receipt.inner.clone() {
InnerReceipt::Fake(receipt) => {
let seal = receipt.claim.digest().as_bytes().to_vec();
let selector = &[0u8; 4];
// Create a new vector with the capacity to hold both selector and seal
let mut selector_seal = Vec::with_capacity(selector.len() + seal.len());
selector_seal.extend_from_slice(selector);
selector_seal.extend_from_slice(&seal);
selector_seal
}
InnerReceipt::Groth16(receipt) => {
let selector = &receipt.verifier_parameters.as_bytes()[..4];
// Create a new vector with the capacity to hold both selector and seal
let mut selector_seal = Vec::with_capacity(selector.len() + receipt.seal.len());
selector_seal.extend_from_slice(selector);
selector_seal.extend_from_slice(receipt.seal.as_ref());
selector_seal
}
_ => bail!("Unsupported receipt type"),
};
Ok(seal)
}
8 changes: 5 additions & 3 deletions examples/erc20-counter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,19 @@ risc0-build = { git = "https://github.com/risc0/risc0", branch = "main", feature
risc0-zkvm = { git = "https://github.com/risc0/risc0", branch = "main", default-features = false }
risc0-zkp = { git = "https://github.com/risc0/risc0", branch = "main", default-features = false }

alloy-primitives = { version = "0.7", default-features = false, features = ["rlp", "serde", "std"] }
alloy = { version = "0.2.1", features = ["full"] }
alloy-primitives = { version = "0.7", features = ["rlp", "serde", "std"] }
alloy-sol-types = { version = "0.7" }
anyhow = { version = "1.0.75" }
bincode = { version = "1.3" }
bytemuck = { version = "1.14" }
ethers = { version = "2.0" }
clap = { version = "4.5" }
hex = { version = "0.4" }
log = { version = "0.4" }
erc20-counter-methods = { path = "./methods" }
serde = { version = "1.0", features = ["derive", "std"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tokio = { version = "1.39", features = ["full"] }
url = { version = "2.5" }

[profile.release]
debug = 1
Expand Down
29 changes: 6 additions & 23 deletions examples/erc20-counter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,10 @@ The contract includes functionality to query the current value of the counter at

## Dependencies

First, [install Rust] and [Foundry], and then restart your terminal.

```sh
# Install Rust
curl https://sh.rustup.rs -sSf | sh
# Install Foundry
curl -L https://foundry.paradigm.xyz | bash
```

Next, you will need to install the `cargo risczero` tool.
We'll use [`cargo binstall`][cargo-binstall] to get `cargo-risczero` installed, and then install the `risc0` toolchain.
See [RISC Zero installation] for more details.

```sh
cargo install cargo-binstall
cargo binstall cargo-risczero
cargo risczero install
```

Now you have all the tools you need to develop and deploy an application with [RISC Zero].
To get started, you need to have the following installed:
- [Rust]
- [Foundry]
- [RISC Zero]

### Configuring Bonsai

Expand All @@ -73,11 +57,10 @@ When you're ready, follow the [deployment guide] to get your application running

[Foundry]: https://getfoundry.sh/
[Groth16 SNARK proof]: https://www.risczero.com/news/on-chain-verification
[RISC Zero installation]: https://dev.risczero.com/api/zkvm/install
[RISC Zero]: https://www.risczero.com/
[RISC Zero]: https://dev.risczero.com/api/zkvm/install
[Sepolia]: https://www.alchemy.com/overviews/sepolia-testnet
[cargo-binstall]: https://github.com/cargo-bins/cargo-binstall#cargo-binaryinstall
[deployment guide]: ./deployment-guide.md
[install Rust]: https://doc.rust-lang.org/cargo/getting-started/installation.html
[Rust]: https://doc.rust-lang.org/cargo/getting-started/installation.html
[Counter]: ./contracts/Counter.sol
[Steel]: https://www.risczero.com/blog/introducing-steel
9 changes: 4 additions & 5 deletions examples/erc20-counter/apps/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ version = { workspace = true }
edition = { workspace = true }

[dependencies]
alloy = { workspace = true }
alloy-primitives = { workspace = true }
alloy-sol-types = { workspace = true }
anyhow = { workspace = true }
clap = { version = "4.0", features = ["derive", "env"] }
clap = { workspace = true, features = ["derive", "env"] }
erc20-counter-methods = { workspace = true }
ethers = { workspace = true }
log = { workspace = true }
risc0-ethereum-contracts = { workspace = true }
risc0-steel = { workspace = true, features = ["host"] }
risc0-zkvm = { workspace = true, features = ["client"] }
tokio = { version = "1.35", features = ["full"] }
tokio = { workspace = true }
tracing-subscriber = { workspace = true }
url = { workspace = true }
14 changes: 4 additions & 10 deletions examples/erc20-counter/apps/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ cargo run --bin publisher
```text
$ cargo run --bin publisher -- --help
Usage: publisher --chain-id <CHAIN_ID> --eth-wallet-private-key <ETH_WALLET_PRIVATE_KEY> --rpc-url <RPC_URL> --contract <CONTRACT> --account <ACCOUNT>
Usage: publisher --eth-wallet-private-key <ETH_WALLET_PRIVATE_KEY> --rpc-url <RPC_URL> --contract <CONTRACT> --token <TOKEN> --account <ACCOUNT>
Options:
--chain-id <CHAIN_ID>
Ethereum chain ID
--eth-wallet-private-key <ETH_WALLET_PRIVATE_KEY>
Ethereum Node endpoint [env: ETH_WALLET_PRIVATE_KEY=0x2a5369d12693b5a8c4e1d0e85788bea5ccb1c90fb6f82bb33a25a216e6cce071]
Ethereum Node endpoint [env: ETH_WALLET_PRIVATE_KEY=]
--rpc-url <RPC_URL>
Ethereum Node endpoint [env: RPC_URL=]
--contract <CONTRACT>
Counter's contract address on Ethereum
--token <TOKEN>
ERC20 contract address on Ethereum
--account <ACCOUNT>
Account address to read the balance_of on Ethereum
-h, --help
Expand All @@ -34,11 +34,5 @@ Options:
Print version
```

## Library

We provide a small rust [library] containing utility functions to help with sending
transactions to a deployed app contract on Ethereum.

[publisher]: ./src/bin/publisher.rs
[library]: ./src/lib.rs
[Counter]: ../contracts/Counter.sol
Loading

0 comments on commit b2dcc8e

Please sign in to comment.