Skip to content

Commit 6ccad76

Browse files
authored
docs: add a book (#41)
* docs: init the book * docs: skeletom * docs: build the book * fix: broken links * fix: base url * fix: nits * feat: explain execution model and proof verification * feat: document how to query event logs * feat: add note on RPC config * feat: add examples * feat: update README * doc: buid and deploy API reference * fix: trailing slash * fix: remove unused code * doc: link to API docs * doc: don't throw on broken links * fix: API links * doc: attempt to better handle relative links * fix: another attempt * fix: use pathname * fix: attempt to streamline links * doc: add links to API * doc: feedbacks * doc: cleanup * doc: add API docs * doc: feedbacks * fix: typo
1 parent 19c0343 commit 6ccad76

29 files changed

+30239
-219
lines changed

.github/workflows/book.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: book
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
paths:
9+
- "book/**"
10+
merge_group:
11+
12+
jobs:
13+
build:
14+
name: Build Docusaurus
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v4
18+
with:
19+
fetch-depth: 0
20+
- uses: actions/setup-node@v4
21+
with:
22+
node-version: 18
23+
24+
- name: Install dependencies
25+
run: cd book && yarn install --frozen-lockfile
26+
- name: Build website
27+
run: |
28+
cd book
29+
yarn build-api
30+
mv ../target/doc ./static/api
31+
mv ./static/api/static.files/* ./static/api
32+
rmdir ./static/api/static.files
33+
yarn build-book
34+
35+
- name: Upload Build Artifact
36+
uses: actions/upload-pages-artifact@v3
37+
with:
38+
path: book/build
39+
40+
deploy:
41+
name: Deploy to GitHub Pages
42+
needs: build
43+
44+
# Grant GITHUB_TOKEN the permissions required to make a Pages deployment
45+
permissions:
46+
pages: write # to deploy to Pages
47+
id-token: write # to verify the deployment originates from an appropriate source
48+
49+
# Deploy to the github-pages environment
50+
environment:
51+
name: github-pages
52+
url: ${{ steps.deployment.outputs.page_url }}
53+
54+
runs-on: ubuntu-latest
55+
steps:
56+
- name: Deploy to GitHub Pages
57+
id: deployment
58+
uses: actions/deploy-pages@v4

README.md

Lines changed: 3 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -2,166 +2,15 @@
22

33
Generates zero-knowledge proofs of Ethereum smart contract execution.
44

5+
[Documentation](https://succinctlabs.github.io/sp1-contract-call/)
6+
57
> [!CAUTION]
68
>
79
> This repository is not meant for production usage.
810
911
## Overview
1012

11-
This library (`sp1-contract-call`, or `sp1-cc` for short), provides developers with a simple interface to efficiently generate a ZKP of Ethereum smart contract execution offchain, that can be verified cheaply onchain for ~280k gas. This enables developers to verifiably run very expensive Solidity smart contract calls and be able to use this information in their onchain applications. Developers simply specific their Solidity function interface in Rust using the [`alloy_sol_macro`](https://docs.rs/alloy-sol-macro/latest/alloy_sol_macro/) library and can write an SP1 program to generate these proofs. Let's check out an example below:
12-
13-
### Client
14-
15-
First, we create a Rust program that runs the Solidity smart contract call, using the `alloy_sol_macro` interface, the contract address and the caller address. This is known as a "client" program and it is run inside SP1 to generate a ZKP of the smart contract call's execution.
16-
17-
In this example, we use the `slot0` function to fetch the current price of the UNI/WETH pair on the UniswapV3 pool. Note that we abi encode the `public_values` -- this is to make it easy later to use those public values on chain. The code below is taken from [`examples/uniswap/client/src/main.rs`](./examples/uniswap/client/src/main.rs) which contains all of the code needed for the SP1 client program.
18-
19-
```rs
20-
sol! {
21-
/// Simplified interface of the IUniswapV3PoolState interface.
22-
interface IUniswapV3PoolState {
23-
function slot0(
24-
) external view returns (uint160 sqrtPriceX96, ...);
25-
}
26-
}
27-
28-
/// Address of Uniswap V3 pool.
29-
const CONTRACT: Address = address!("1d42064Fc4Beb5F8aAF85F4617AE8b3b5B8Bd801");
30-
31-
...
32-
33-
let state_sketch_bytes = sp1_zkvm::io::read::<Vec<u8>>();
34-
let state_sketch = bincode::deserialize::<EVMStateSketch>(&state_sketch_bytes).unwrap();
35-
36-
// Initialize the client executor with the state sketch.
37-
// This step also validates all of the storage against the provided state root.
38-
let executor = ClientExecutor::eth(state_sketch).unwrap();
39-
40-
// Execute the slot0 call using the client executor.
41-
let slot0_call = IUniswapV3PoolState::slot0Call {};
42-
let call = ContractInput::new_call(CONTRACT, Address::default(), slot0_call);
43-
let public_vals = executor.execute(call).unwrap();
44-
45-
// Commit the abi-encoded output.
46-
sp1_zkvm::io::commit_slice(&public_vals.abi_encode());
47-
```
48-
49-
### Host
50-
51-
Under the hood, the SP1 client program uses the executor from the `sp1-cc-client-executor` library, which requires storage slots and merkle proof information to correctly and verifiably run the smart contract execution.
52-
53-
The "host" program is code that is run outside of the zkVM & is responsible for fetching all of the witness data that is needed for the client program. This witness data includes storage slots, account information & merkle proofs that the client program verifies.
54-
55-
You can see in the host example code below that we run the exact same contract call with the host executor (instead of the client executor), and the host executor will fetch all relevant information as its executing. When we call `finalize()` on the host executor, it prepares all of the data it has gathered during contract call execution and then prepares it for input into the client program.
56-
57-
```rs
58-
...
59-
60-
// Prepare the host executor.
61-
//
62-
// Use `RPC_URL` to get all of the necessary state for the smart contract call.
63-
let rpc_url = std::env::var("RPC_URL").unwrap_or_else(|_| panic!("Missing RPC_URL"));
64-
let provider = ReqwestProvider::new_http(Url::parse(&rpc_url)?);
65-
let mut host_executor = HostExecutor::new(provider.clone(), block_number).await?;
66-
67-
// Keep track of the block hash. We'll later validate the client's execution against this.
68-
let block_hash = host_executor.header.hash_slow();
69-
70-
// Make the call to the slot0 function.
71-
let slot0_call = IUniswapV3PoolState::slot0Call {};
72-
let _price_x96_bytes = host_executor
73-
.execute(ContractInput::new_call(CONTRACT, Address::default(), slot0_call))
74-
.await?;
75-
76-
// Now that we've executed all of the calls, get the `EVMStateSketch` from the host executor.
77-
let input = host_executor.finalize().await?;
78-
79-
// Feed the sketch into the client.
80-
let input_bytes = bincode::serialize(&input)?;
81-
let mut stdin = SP1Stdin::new();
82-
stdin.write(&input_bytes);
83-
84-
// Now we can call the client program.
85-
86-
...
87-
88-
```
89-
90-
After running the client program in the host, we generate a proof that can easily be verified on chain. In addition, the public values associated with our proof are abi-encoded, which allows us to use the output of the contract call on chain. Here is part of a sample contract that verifies this proof; check out [`examples/uniswap/contracts`](./examples/uniswap/contracts/) for more details.
91-
92-
```sol
93-
/// @title SP1 UniswapCall.
94-
/// @notice This contract implements a simple example of verifying the proof of call to a smart
95-
/// contract.
96-
contract UniswapCall {
97-
/// @notice The address of the SP1 verifier contract.
98-
/// @dev This can either be a specific SP1Verifier for a specific version, or the
99-
/// SP1VerifierGateway which can be used to verify proofs for any version of SP1.
100-
/// For the list of supported verifiers on each chain, see:
101-
/// https://github.com/succinctlabs/sp1-contracts/tree/main/contracts/deployments
102-
address public verifier;
103-
104-
/// @notice The verification key for the uniswapCall program.
105-
bytes32 public uniswapCallProgramVKey;
106-
107-
constructor(address _verifier, bytes32 _uniswapCallProgramVKey) {
108-
verifier = _verifier;
109-
uniswapCallProgramVKey = _uniswapCallProgramVKey;
110-
}
111-
112-
/// @notice The entrypoint for verifying the proof of a uniswapCall number.
113-
/// @param _proofBytes The encoded proof.
114-
/// @param _publicValues The encoded public values.
115-
function verifyUniswapCallProof(bytes calldata _publicValues, bytes calldata _proofBytes)
116-
public
117-
view
118-
returns (uint160)
119-
{
120-
ISP1Verifier(verifier).verifyProof(uniswapCallProgramVKey, _publicValues, _proofBytes);
121-
ContractPublicValues memory publicValues = abi.decode(_publicValues, (ContractPublicValues));
122-
uint160 sqrtPriceX96 = abi.decode(publicValues.contractOutput, (uint160));
123-
return sqrtPriceX96;
124-
}
125-
}
126-
```
127-
## Running examples
128-
129-
To use SP1-contract-call, you must first have Rust installed and SP1 installed to build the client programs. In addition, you need to set the `ETH_RPC_URL` and `ETH_SEPOLIA_RPC_URL` environment variables. You can do this manually by running the following:
130-
131-
```
132-
export ETH_RPC_URL=[YOUR ETH RPC URL HERE]
133-
export ETH_SEPOLIA_RPC_URL=[YOUR ETH SEPOLIA RPC URL HERE]
134-
```
135-
136-
Alternatively, you can use a `.env` file (see [example](./.env.example)).
137-
138-
Then, from the root directory of the repository, run
139-
140-
```RUST_LOG=info cargo run --bin [example] --release```
141-
142-
where `[example]` is one of the following
143-
* `uniswap-basic`
144-
* Fetches the price of the UNI / WETH pair on Uniswap V3. By default, this does not generate a proof.
145-
* Running `RUST_LOG=info cargo run --bin [example] --release -- --prove` will generate a plonk proof. This requires
146-
significant computational resources, so we recommend using the [SP1 Prover network](https://docs.succinct.xyz/docs/generating-proofs/prover-network).
147-
* 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.
148-
* `uniswap-onchain-verify`
149-
* Fetches the price of the WETH / USDC pair on Uniswap V3 on Sepolia.
150-
* This example demonstrate on-chain verification, with the following variations:
151-
* By default, the `blockhash()` opcode is used, allowing to verify up to 256 blocks old.
152-
* If you provides a Beacon RPC endpoint with the `--beacon-sepolia-rpc-url` argument, the proof will be verified on chain with the beacon root using [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788), up to 8191 blocks old (~27h).
153-
* The window can even be extended up to the Cancun hardfork by chaining beacon roots (see the `--reference-block` argument).
154-
* The contract can be found at the [contracts](./examples/uniswap/contracts/) directory.
155-
* `multiplexer`
156-
* Calls a contract that fetches the prices of many different collateral assets.
157-
* The source code of this contract is found [here](./examples/multiplexer/ZkOracleHelper.sol).
158-
* Due to the size of this program, it's recommended to use the [SP1 Prover network](https://docs.succinct.xyz/docs/generating-proofs/prover-network) to generate proofs for this example.
159-
* `verify-quorum`
160-
* Calls a contract that verifies several ECDSA signatures on chain, and sums the stake for the addresses corresponding to valid signatures.
161-
* `example-deploy`
162-
* Demonstrates how to simulate a contract creation transaction on SP1-CC.
163-
* `events`
164-
* Demonstrates how to prefetch log events in the host and send them to the client as inputs.
13+
This library (`sp1-contract-call`, or `sp1-cc` for short), provides developers with a simple interface to efficiently generate a ZKP of Ethereum smart contract execution offchain, that can be verified cheaply onchain for ~280k gas. This enables developers to verifiably run very expensive Solidity smart contract calls and be able to use this information in their onchain applications. Developers simply specify their Solidity function interface in Rust using the [`alloy_sol_macro`](https://docs.rs/alloy-sol-macro/latest/alloy_sol_macro/) library and can write an SP1 program to generate these proofs. Let's check out an example below:
16514

16615
## Acknowledgments
16716

book/.gitignore

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Dependencies
2+
/node_modules
3+
4+
# Production
5+
/build
6+
7+
# Generated files
8+
.docusaurus
9+
.cache-loader
10+
11+
# Misc
12+
.DS_Store
13+
.env.local
14+
.env.development.local
15+
.env.test.local
16+
.env.production.local
17+
18+
npm-debug.log*
19+
yarn-debug.log*
20+
yarn-error.log*

book/README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Website
2+
3+
This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
4+
5+
### Installation
6+
7+
```
8+
$ yarn
9+
```
10+
11+
### Local Development
12+
13+
```
14+
$ yarn start
15+
```
16+
17+
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
18+
19+
### Build
20+
21+
```
22+
$ yarn build
23+
```
24+
25+
This command generates static content into the `build` directory and can be served using any static contents hosting service.
26+
27+
### Deployment
28+
29+
Using SSH:
30+
31+
```
32+
$ USE_SSH=true yarn deploy
33+
```
34+
35+
Not using SSH:
36+
37+
```
38+
$ GIT_USER=<Your GitHub username> yarn deploy
39+
```
40+
41+
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.

book/docs/custom-chains.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
title: Custom chains
3+
sidebar_position: 8
4+
---
5+
6+
If you want to run SP1 Contract Call on a custom EVM chain, you can use the [`with_genesis()`] function while [building the `EvmSketch`](https://succinctlabs.github.io/sp1-contract-call/api/sp1_cc_host_executor/struct.EvmSketch.html#method.builder). The [`Genesis`] enum allows to specify a custom chain by using its genesis JSON.
7+
8+
:::tip
9+
10+
You can find examples of genesis JSON [here](https://github.com/succinctlabs/rsp/tree/main/bin/host/genesis).
11+
12+
:::
13+
14+
15+
16+
17+
18+
[`with_genesis()`]: pathname:///api/sp1_cc_host_executor/struct.EvmSketchBuilder.html#method.with_genesis
19+
[`Genesis`]: pathname:///api/sp1_cc_host_executor/enum.Genesis.html

book/docs/events.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
title: Events
3+
sidebar_position: 6
4+
---
5+
6+
## Overview
7+
8+
Event logs are a fundamental component of the Ethereum blockchain that allow smart contracts to emit structured data during transaction execution. They serve as an efficient way to store and retrieve information about contract interactions.
9+
10+
SP1 Contract Call enables zero-knowledge verification of Ethereum event logs by fetching transaction receipts and their associated logs, then proving their validity within the zkVM. This allows applications to trustlessly verify that specific events occurred on the blockchain without requiring direct access to the Ethereum network.
11+
12+
## How to query event logs
13+
14+
### Events declaration
15+
16+
To be able to query events, you first need to declare them using [Alloy `sol!` macro](https://alloy.rs/contract-interactions/using-sol!#using-the-sol-macro):
17+
18+
```rust
19+
sol! {
20+
interface IERC20 {
21+
event Transfer(address indexed from, address indexed to, uint256 value);
22+
}
23+
}
24+
```
25+
26+
You'll need to interact with the module generated by the `sol!` macro in both the host and the client, so it's a good practice to have the code above in a shared lib accessible in both environments.
27+
28+
### Host-Side Event Processing
29+
30+
Events logs are prefetched in the host with `EvmSketch::get_logs()`. You just need to build a [`Filter`](https://docs.rs/alloy/latest/alloy/rpc/types/struct.Filter.html) and call `get_log()`, like in the example below:
31+
32+
```rust
33+
let filter = Filter::new()
34+
.address(WETH)
35+
.at_block_hash(sketch.anchor.header().hash_slow())
36+
.event(IERC20::Transfer::SIGNATURE);
37+
38+
let event_logs = sketch.get_logs(&filter).await.unwrap();
39+
```
40+
41+
Besides returning the requested event logs, `get_log()` records the corresponding receipts in order to later include them in the `EvmSketchInput` when `EvmSketch::finalize()` is called.
42+
43+
You can find more info about how to query event logs in the [Alloy documentation](https://alloy.rs/contract-interactions/queries#query-logs).
44+
45+
### zkVM Execution
46+
47+
In the client, the event logs can be easily queried, like in the example below:
48+
49+
```rust
50+
let executor = ClientExecutor::new(&state_sketch).unwrap();
51+
let filter = Filter::new().address(WETH).event(IERC20::Transfer::SIGNATURE);
52+
let logs = executor.get_logs::<IERC20::Transfer>(filter).unwrap();
53+
```
54+

0 commit comments

Comments
 (0)