Skip to content

basic snark cosmwasm verifier #36

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
4 changes: 4 additions & 0 deletions cosmwasm-verifier/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
target
factors_id.json
stark-receipt.json
snark-receipt.json
7 changes: 7 additions & 0 deletions cosmwasm-verifier/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"rust-analyzer.linkedProjects": [
"./risc0-example/methods/guest/Cargo.toml",
"./risc0-example/host/Cargo.toml",
"./cosmwasm/Cargo.toml",
]
}
99 changes: 99 additions & 0 deletions cosmwasm-verifier/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# CosmWasm verifier

### Running

Build image ID and receipt to verify:

```console
cd risc0-example
cargo run
```

Optionally set variables from the output files:

```console
read -x IMAGE_ID < ../factors_id.json
# read -x STARK_RECEIPT < ../stark-receipt.json
read -x SNARK_RECEIPT < ../snark-receipt.json
```


Install Wasmd and other dependencies (v0.52.0):

https://docs.cosmwasm.com/docs/getting-started/installation

(Optional) Initialize a wallet:

```console
wasm keys add wallet
```

Init network:

```
wasmd init localnet --chain-id localnet
wasmd genesis add-genesis-account $(wasmd keys show -a wallet) 10000000000uwasm,10000000000stake
wasmd genesis gentx wallet 10000000000stake --chain-id localnet
wasmd genesis collect-gentxs
wasmd genesis validate-genesis
wasmd start
```

Build the contract:

in `./cosmwasm/`

x86_64:

```console
docker run --rm -v "$(pwd)":/code \
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
cosmwasm/optimizer:0.16.0
```

ARM:

```console
docker run --rm -v "$(pwd)":/code \
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
cosmwasm/optimizer-arm64:0.16.0
```

Deploy contract:

```console
set -x RES $(wasmd tx wasm store artifacts/cosmwasm_verifier.wasm --from wallet --gas-prices 0.1uwasm --gas auto --gas-adjustment 2 -y --output json -b sync --node http://127.0.0.1:26657 --chain-id localnet)

# For bash:
export CODE_ID=$(wasmd query wasm list-code --output json | jq -r '.code_infos[0].code_id')

# Fish
set -x CODE_ID $(wasmd query wasm list-code --output json | jq -r '.code_infos[0].code_id')
```

Initialize contract:

```console
wasmd tx wasm instantiate $CODE_ID "{\"image_id\":$IMAGE_ID}" --from wallet --label "Initialize with image ID" --gas-prices 0.025uwasm --gas auto --gas-adjustment 2 -b sync -y --no-admin --chain-id localnet --node http://127.0.0.1:26657
```


Get contract address:

```console
set -x CONTRACT_ADDR $(wasmd query wasm list-contract-by-code $CODE_ID --output json --node http://127.0.0.1:26657 | jq -r '.contracts[0]')
```

Query the contract with a receipt:

```console
wasmd query wasm contract-state smart $CONTRACT_ADDR "{\"Verify\":{\"receipt\":$SNARK_RECEIPT}}" --output json --node http://127.0.0.1:26657
```

Sending a transaction verifying the receipt:

```console
wasmd tx wasm execute $CONTRACT_ADDR "{\"Verify\":{\"receipt\":$SNARK_RECEIPT}}" --from wallet --gas-prices 0.025uwasm --gas auto --gas-adjustment 1.3 -y --chain-id localnet --output json --node http://127.0.0.1:26657
```
3 changes: 3 additions & 0 deletions cosmwasm-verifier/cosmwasm/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[alias]
wasm = "build --target wasm32-unknown-unknown --release"
wasm-debug = "build --target wasm32-unknown-unknown"
2 changes: 2 additions & 0 deletions cosmwasm-verifier/cosmwasm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target
artifacts
27 changes: 27 additions & 0 deletions cosmwasm-verifier/cosmwasm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "cosmwasm-verifier"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
cosmwasm-std = { version = "1.0.0-beta8", features = ["staking"] }
serde = { version = "1.0.103", default-features = false, features = [
"derive",
"rc",
] }
risc0-zkvm = { version = "=1.0.5", default-features = false }
cw-storage-plus = "0.13.4"
serde_json = "1.0.111"

[dev-dependencies]
cw-multi-test = "0.13.4"

[profile.release]
codegen-units = 1
opt-level = "z"
lto = "thin"
debug = false
panic = "abort"
117 changes: 117 additions & 0 deletions cosmwasm-verifier/cosmwasm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use std::rc::Rc;

use cosmwasm_std::{
entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError,
StdResult,
};
use cw_storage_plus::Item;
use risc0_zkvm::{sha::Digestible, Groth16Receipt, Journal, MaybePruned, ReceiptClaim};
use serde::{Deserialize, Serialize};

pub const IMAGE_ID: Item<[u32; 8]> = Item::new("image_id");

#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
pub struct InstantiateMsg {
pub image_id: [u32; 8],
}

#[entry_point]
pub fn instantiate(
deps: DepsMut,
_env: Env,
_info: MessageInfo,
msg: InstantiateMsg,
) -> StdResult<Response> {
IMAGE_ID.save(deps.storage, &msg.image_id)?;

Ok(Response::new())
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum QueryMsg {
Verify {
receipt: Rc<(Groth16Receipt<ReceiptClaim>, Journal)>,
},
}

#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {
QueryMsg::Verify { receipt } => verify(deps, &receipt),
}
}

#[entry_point]
pub fn execute(deps: DepsMut, _env: Env, _info: MessageInfo, msg: QueryMsg) -> StdResult<Response> {
match msg {
QueryMsg::Verify { receipt } => {
verify(deps.as_ref(), &receipt).map(|res| Response::new().set_data(res))
}
}
}

fn verify(
deps: Deps,
(receipt, journal): &(Groth16Receipt<ReceiptClaim>, Journal),
) -> StdResult<Binary> {
let image_id = IMAGE_ID.load(deps.storage)?;

// Verify receipt.
receipt.verify_integrity().unwrap();

// Check that the claim on the verified receipt matches what was expected. Since we have
// constrained all field in the ReceiptClaim, we can directly construct the expected digest
// and do not need to open the claim digest on the inner receipt.
let expected_claim = ReceiptClaim::ok(image_id, MaybePruned::Pruned(journal.digest()));
if expected_claim.digest() != receipt.claim.digest() {
return Err(StdError::generic_err("Claim digest mismatch"));
}

to_json_binary(journal.bytes.as_slice())
}

#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::Addr;
use cw_multi_test::{App, ContractWrapper, Executor};

#[test]
fn verify_query() {
// Read image_id from JSON file
let image_id_json = include_bytes!("../../factors_id.json");
let image_id: [u32; 8] = serde_json::from_slice(image_id_json).unwrap();

// Read receipt from JSON file
let receipt_json = include_bytes!("../../snark-receipt.json");
let receipt = serde_json::from_slice(receipt_json).unwrap();

let mut app = App::default();

let code = ContractWrapper::new(execute, instantiate, query);
let code_id = app.store_code(Box::new(code));

let addr = app
.instantiate_contract(
code_id,
Addr::unchecked("owner"),
&InstantiateMsg { image_id },
&[],
"Contract",
None,
)
.unwrap();

let resp: Vec<u8> = app
.wrap()
.query_wasm_smart(
addr,
&QueryMsg::Verify {
receipt: Rc::new(receipt),
},
)
.unwrap();

assert_eq!([63, 0, 0, 0, 0, 0, 0, 0].as_slice(), &resp);
}
}
4 changes: 4 additions & 0 deletions cosmwasm-verifier/risc0-example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.DS_Store
Cargo.lock
methods/guest/Cargo.lock
target/
17 changes: 17 additions & 0 deletions cosmwasm-verifier/risc0-example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[workspace]
resolver = "2"
members = ["host", "methods"]

# Always optimize; building and running the guest takes much longer without optimization.
[profile.dev]
opt-level = 3

[profile.dev.build-override]
opt-level = 3

[profile.release]
debug = 1
lto = true

[profile.release.build-override]
opt-level = 3
Loading