Skip to content

Commit

Permalink
import from abi
Browse files Browse the repository at this point in the history
  • Loading branch information
MaanavKhaitan committed Nov 14, 2024
1 parent b9998ce commit 2a26949
Show file tree
Hide file tree
Showing 8 changed files with 1,068 additions and 141 deletions.
1,075 changes: 1,045 additions & 30 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ alloy-sol-types = { version = "0.7", default-features = false }

clap = { version = "4", features = ["derive"] }

ivm-abi = { git = "https://github.com/InfinityVM/InfinityVM.git", rev = "f360c96" }
zkvm-executor = { git = "https://github.com/InfinityVM/InfinityVM.git", branch = "sp1-feature-branch" }

k256 = { version = "0.13", default-features = false }

risc0-zkvm = { version = "1", features = ["prove"], default-features = false }
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The flow of the InfinityVM coprocessor looks like this:
2. The coprocessor executes this job and submits the result back to the contract.
3. The app contract can simply use the result from the coprocessor in any of their app logic.

![InfinityVM coprocessor flow](images/overview.png)
![InfinityVM coprocessor flow](assets/overview.png)

## Quick Start

Expand Down Expand Up @@ -82,7 +82,7 @@ We can call the `square_root.rs` program from our app contract. We just need to
1. Call `requestJob()` with the program ID of `square_root.rs` from `ProgramID.sol` along with ABI-encoded inputs (the number we want to calculate the square root of).
2. Write a `_receiveResult()` function which accepts the output from the `square_root.rs` program and uses it in some application logic.

![Onchain request flow](images/onchain-request.png)
![Onchain request flow](assets/onchain-request.png)

To build the contracts, you can run:
```
Expand All @@ -93,7 +93,7 @@ forge build

We can also call the `square_root.rs` program offchain by sending a request directly to the coprocessor. The coprocessor will execute the job and submit the result to our app contract. The flow looks like this:

![Offchain request flow](images/offchain-request.png)
![Offchain request flow](assets/offchain-request.png)

The offchain request to the coprocessor can be sent by an app, a user, or any authorized third-party. To support offchain requests for your app contract, you need to implement the `isValidSignature()` function in your contract, which is called to verify whether an offchain request is signed by an authorized signer/user. We've provided an example implementation of `isValidSignature()` in the `SquareRootConsumer.sol` contract (which checks that each job request is signed by a signer owned by the app), but you can implement any logic or checks you'd like.

Expand Down
File renamed without changes
File renamed without changes
File renamed without changes
3 changes: 3 additions & 0 deletions zkvm-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ risc0-zkp = { workspace = true }
risc0-zkvm = { workspace = true, features = ["client"] }
tokio = { workspace = true }

ivm-abi = { workspace = true }
zkvm-executor = { workspace = true }

sp1-zkvm = { workspace = true }
sp1-sdk = { workspace = true }
sp1-helper = { workspace = true }
Expand Down
122 changes: 14 additions & 108 deletions zkvm-utils/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ use alloy::{
use alloy_sol_types::{sol, SolType};
use anyhow::{Context, Result};
use clap::Parser;
use ivm_abi::{
get_job_id, JobParams, abi_encode_offchain_job_request, abi_encode_result_with_metadata,
abi_encode_offchain_result_with_metadata,
};
use k256::ecdsa::SigningKey;
use risc0_binfmt::compute_image_id;
use risc0_zkvm::{Executor, ExecutorEnv, LocalProver};
Expand Down Expand Up @@ -117,10 +121,10 @@ async fn execute_onchain_job_ffi(
let journal = execute_onchain_job(&elf, &onchain_input, max_cycles)?;
let result_with_metadata = abi_encode_result_with_metadata(
job_id,
onchain_input,
onchain_input.as_slice().try_into().unwrap(),
max_cycles,
program_id_bytes,
journal,
&journal,
);

let zkvm_operator_signature = sign_message(&result_with_metadata, signer).await?;
Expand Down Expand Up @@ -157,14 +161,15 @@ async fn execute_offchain_job_ffi(
// Create a signed job request
// This would normally be sent by the user/app signer, but we
// construct it here to work with the foundry tests.
let job_request = abi_encode_offchain_job_request(
let job_params = JobParams {
nonce,
max_cycles,
&consumer,
program_id_bytes,
onchain_input.clone(),
offchain_input_hash.into(),
);
consumer_address: **Address::from_str(&consumer).unwrap(),
program_id: program_id_bytes,
onchain_input: &onchain_input,
offchain_input_hash: offchain_input_hash.into(),
};
let job_request = abi_encode_offchain_job_request(job_params);

let offchain_signer = create_signer(&secret)?;
let offchain_signer_signature = sign_message(&job_request, &offchain_signer).await?;
Expand All @@ -181,7 +186,7 @@ async fn execute_offchain_job_ffi(
offchain_input_hash.into(),
max_cycles,
program_id_bytes,
journal,
&journal,
versioned_blob_hashes,
);
let zkvm_operator_signature = sign_message(&offchain_result_with_metadata, signer).await?;
Expand Down Expand Up @@ -255,30 +260,6 @@ pub type OffChainResultWithSignatureCalldata = sol! {
tuple(bytes,bytes,bytes,bytes)
};

/// The payload that gets signed to signify that the zkvm executor has faithfully
/// executed an onchain job. Also the result payload the job manager contract expects.
///
/// tuple(JobID,OnchainInputHash,MaxCycles,VerifyingKey,RawOutput)
pub type ResultWithMetadata = sol! {
tuple(bytes32,bytes32,uint64,bytes32,bytes)
};

/// The payload that gets signed to signify that the zkvm executor has faithfully
/// executed an offchain job. Also the result payload the job manager contract expects.
///
/// tuple(JobID,OnchainInputHash,OffchainInputHash,OffchainStateHash,MaxCycles,VerifyingKey,
/// RawOutput,VersionedBlobHashes)
pub type OffChainResultWithMetadata = sol! {
tuple(bytes32,bytes32,bytes32,uint64,bytes32,bytes,bytes32[])
};

/// The payload that gets signed by the entity sending an offchain job request.
/// This can be the user but the Consumer contract can decide who needs to
/// sign this request.
pub type OffchainJobRequest = sol! {
tuple(uint64,uint64,address,bytes32,bytes,bytes32)
};

/// Returns ABI-encoded calldata with result and signature. This ABI-encoded response will be
/// sent to the `JobManager` contract.
pub fn abi_encode_result_with_signature_calldata(result: Vec<u8>, signature: Vec<u8>) -> Vec<u8> {
Expand All @@ -301,81 +282,6 @@ pub fn abi_encode_offchain_result_with_signature_calldata(
))
}

/// Returns an ABI-encoded result with metadata. This ABI-encoded response will be
/// signed by the coprocessor operator.
pub fn abi_encode_result_with_metadata(
job_id: [u8; 32],
onchain_input: Vec<u8>,
max_cycles: u64,
program_verifying_key: &[u8; 32],
raw_output: Vec<u8>,
) -> Vec<u8> {
let onchain_input_hash = keccak256(onchain_input);
ResultWithMetadata::abi_encode(&(
job_id,
onchain_input_hash,
max_cycles,
program_verifying_key,
raw_output,
))
}

/// Returns an ABI-encoded offchain result with metadata. This ABI-encoded response will be
/// signed by the coprocessor operator.
pub fn abi_encode_offchain_result_with_metadata(
job_id: [u8; 32],
onchain_input_hash: [u8; 32],
offchain_input_hash: [u8; 32],
max_cycles: u64,
program_verifying_key: &[u8; 32],
raw_output: Vec<u8>,
versioned_blob_hashes: Vec<[u8; 32]>,
) -> Vec<u8> {
OffChainResultWithMetadata::abi_encode(&(
job_id,
onchain_input_hash,
offchain_input_hash,
max_cycles,
program_verifying_key,
raw_output,
versioned_blob_hashes,
))
}

/// Returns an ABI-encoded offchain job request. This ABI-encoded request can be
/// signed by the user sending the request, but the Consumer contract can
/// decide who this request should be signed by.
pub fn abi_encode_offchain_job_request(
nonce: u64,
max_cycles: u64,
consumer: &str,
program_verifying_key: &[u8; 32],
onchain_input: Vec<u8>,
offchain_input_hash: [u8; 32],
) -> Vec<u8> {
OffchainJobRequest::abi_encode(&(
nonce,
max_cycles,
Address::from_str(consumer).unwrap(),
program_verifying_key,
onchain_input,
offchain_input_hash,
))
}

type NonceAndConsumer = sol! {
tuple(uint64, address)
};

fn abi_encode_nonce_and_consumer(nonce: u64, consumer: Address) -> Vec<u8> {
NonceAndConsumer::abi_encode_packed(&(nonce, consumer))
}

/// Returns the job ID hash for a given nonce and consumer address.
pub fn get_job_id(nonce: u64, consumer: Address) -> [u8; 32] {
keccak256(abi_encode_nonce_and_consumer(nonce, consumer)).into()
}

fn create_signer(secret: &str) -> Result<LocalSigner<SigningKey>> {
let hex = if let Some(stripped) = secret.strip_prefix("0x") { stripped } else { secret };
let decoded = hex::decode(hex)?;
Expand Down

0 comments on commit 2a26949

Please sign in to comment.