Quartz based MTCS enclave impl (#50)

This commit is contained in:
Shoaib Ahmed 2024-03-20 02:39:55 +05:30 committed by GitHub
commit 76dd7465ae
19 changed files with 1168 additions and 50 deletions

268
Cargo.lock generated
View file

@ -157,7 +157,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
@ -168,7 +168,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
@ -473,7 +473,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
@ -832,6 +832,38 @@ dependencies = [
"serde",
]
[[package]]
name = "cw-tee-mtcs"
version = "0.1.0"
source = "git+ssh://git@github.com/informalsystems/bisenzone-cw-mvp.git?branch=hu55a1n1/11-use-quartz#cbf2f52a82247606d700a65d8ddca75b4a769b71"
dependencies = [
"cosmwasm-schema",
"cosmwasm-std",
"cw-storage-plus",
"cw2",
"hex",
"k256 0.13.3",
"quartz-cw",
"schemars",
"serde",
"thiserror",
]
[[package]]
name = "cw2"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa"
dependencies = [
"cosmwasm-schema",
"cosmwasm-std",
"cw-storage-plus",
"schemars",
"semver",
"serde",
"thiserror",
]
[[package]]
name = "darling"
version = "0.20.8"
@ -853,7 +885,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim 0.10.0",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
@ -864,7 +896,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
dependencies = [
"darling_core",
"quote",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
@ -948,7 +980,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
@ -979,6 +1011,7 @@ dependencies = [
"digest 0.10.7",
"elliptic-curve 0.13.8",
"rfc6979 0.4.0",
"serdect",
"signature 2.2.0",
"spki 0.7.3",
]
@ -994,6 +1027,7 @@ dependencies = [
"hkdf",
"libsecp256k1",
"once_cell",
"openssl",
"parking_lot",
"rand_core 0.6.4",
"sha2 0.10.8",
@ -1101,6 +1135,7 @@ dependencies = [
"pkcs8 0.10.2",
"rand_core 0.6.4",
"sec1 0.7.3",
"serdect",
"subtle",
"zeroize",
]
@ -1188,6 +1223,21 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
@ -1259,7 +1309,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
@ -1366,7 +1416,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http",
"indexmap 2.2.3",
"indexmap 2.2.4",
"slab",
"tokio",
"tokio-util",
@ -1402,9 +1452,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.8"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "hex"
@ -1580,9 +1630,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.2.3"
version = "2.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
checksum = "967d6dd42f16dbf0eb8040cb9e477933562684d3918f7d253f2ff9087fb3e7a3"
dependencies = [
"equivalent",
"hashbrown 0.14.3",
@ -1659,6 +1709,7 @@ dependencies = [
"ecdsa 0.16.9",
"elliptic-curve 0.13.8",
"once_cell",
"serdect",
"sha2 0.10.8",
"signature 2.2.0",
]
@ -1777,9 +1828,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.20"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "matchers"
@ -1802,6 +1853,14 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "mcmf"
version = "2.0.0"
source = "git+https://github.com/hu55a1n1/flow#cad8f3adb29d3be2177a31db0fa0e5c2a858beb0"
dependencies = [
"cc",
]
[[package]]
name = "memchr"
version = "2.7.1"
@ -1834,6 +1893,20 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "mtcs"
version = "0.1.0"
source = "git+ssh://git@github.com/informalsystems/mtcs.git#b0b90c348f09c10828e321e505220a18ee891c8e"
dependencies = [
"displaydoc",
"itertools 0.10.5",
"log",
"mcmf",
"num-traits",
"petgraph 0.6.3",
"serde",
]
[[package]]
name = "mtcs-enclave"
version = "0.1.0"
@ -1852,6 +1925,21 @@ dependencies = [
"tracing-subscriber",
]
[[package]]
name = "mtcs-intent"
version = "0.1.0"
dependencies = [
"clap",
"cosmwasm-std",
"ecies",
"hex",
"k256 0.13.3",
"rand",
"serde",
"serde_json",
"sha2 0.10.8",
]
[[package]]
name = "multimap"
version = "0.8.3"
@ -1868,6 +1956,20 @@ dependencies = [
"winapi",
]
[[package]]
name = "num"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.4"
@ -1897,6 +1999,15 @@ dependencies = [
"zeroize",
]
[[package]]
name = "num-complex"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6"
dependencies = [
"num-traits",
]
[[package]]
name = "num-conv"
version = "0.1.0"
@ -1934,6 +2045,18 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.18"
@ -1975,12 +2098,50 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
version = "0.10.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
dependencies = [
"bitflags 2.4.2",
"cfg-if 1.0.0",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
]
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "overload"
version = "0.1.1"
@ -2073,6 +2234,16 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "petgraph"
version = "0.6.3"
source = "git+https://github.com/hu55a1n1/petgraph?branch=min-cost-flow-primal-dual#3a2664a3b9d1e5b71c411a6ed497cf576c1a86ae"
dependencies = [
"fixedbitset",
"indexmap 1.9.3",
"num",
]
[[package]]
name = "petgraph"
version = "0.6.4"
@ -2080,7 +2251,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
dependencies = [
"fixedbitset",
"indexmap 2.2.3",
"indexmap 2.2.4",
]
[[package]]
@ -2100,7 +2271,7 @@ checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
@ -2146,6 +2317,12 @@ dependencies = [
"spki 0.7.3",
]
[[package]]
name = "pkg-config"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "polyval"
version = "0.6.1"
@ -2177,7 +2354,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5"
dependencies = [
"proc-macro2",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
@ -2221,12 +2398,12 @@ dependencies = [
"log",
"multimap",
"once_cell",
"petgraph",
"petgraph 0.6.4",
"prettyplease",
"prost 0.12.3",
"prost-types 0.12.3",
"regex",
"syn 2.0.51",
"syn 2.0.52",
"tempfile",
"which",
]
@ -2254,7 +2431,7 @@ dependencies = [
"itertools 0.11.0",
"proc-macro2",
"quote",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
@ -2278,7 +2455,7 @@ dependencies = [
[[package]]
name = "quartz-cw"
version = "0.1.0"
source = "git+ssh://git@github.com/informalsystems/bisenzone-cw-mvp.git?branch=hu55a1n1/11-use-quartz#0e877198416e3c2fb85777c177bcbabb3ca80b69"
source = "git+ssh://git@github.com/informalsystems/bisenzone-cw-mvp.git?branch=hu55a1n1/11-use-quartz#cbf2f52a82247606d700a65d8ddca75b4a769b71"
dependencies = [
"cosmwasm-schema",
"cosmwasm-std",
@ -2298,7 +2475,11 @@ dependencies = [
"clap",
"color-eyre",
"cosmwasm-std",
"cw-proof",
"cw-tee-mtcs",
"ecies",
"k256 0.13.3",
"mtcs",
"prost 0.12.3",
"quartz-cw",
"quartz-proto",
@ -2309,8 +2490,10 @@ dependencies = [
"serde_json",
"tendermint 0.34.0",
"tendermint-light-client",
"tm-stateless-verifier",
"tokio",
"tonic 0.11.0",
"tonic-build",
]
[[package]]
@ -2349,7 +2532,7 @@ dependencies = [
[[package]]
name = "quartz-tee-ra"
version = "0.1.0"
source = "git+ssh://git@github.com/informalsystems/bisenzone-cw-mvp.git?branch=hu55a1n1/11-use-quartz#0e877198416e3c2fb85777c177bcbabb3ca80b69"
source = "git+ssh://git@github.com/informalsystems/bisenzone-cw-mvp.git?branch=hu55a1n1/11-use-quartz#cbf2f52a82247606d700a65d8ddca75b4a769b71"
dependencies = [
"cosmwasm-schema",
"cosmwasm-std",
@ -2743,6 +2926,7 @@ dependencies = [
"der 0.7.8",
"generic-array",
"pkcs8 0.10.2",
"serdect",
"subtle",
"zeroize",
]
@ -2821,7 +3005,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
@ -2854,7 +3038,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
@ -2894,7 +3078,17 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
name = "serdect"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177"
dependencies = [
"base16ct 0.2.0",
"serde",
]
[[package]]
@ -3078,9 +3272,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.51"
version = "2.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c"
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
dependencies = [
"proc-macro2",
"quote",
@ -3414,7 +3608,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
@ -3553,7 +3747,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
@ -3669,7 +3863,7 @@ dependencies = [
"proc-macro2",
"prost-build",
"quote",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
@ -3723,7 +3917,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.51",
"syn 2.0.52",
]
[[package]]
@ -3863,6 +4057,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"
@ -3915,7 +4115,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.51",
"syn 2.0.52",
"wasm-bindgen-shared",
]
@ -3949,7 +4149,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.51",
"syn 2.0.52",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -4172,5 +4372,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.51",
"syn 2.0.52",
]

139
README.md

File diff suppressed because one or more lines are too long

View file

@ -7,6 +7,7 @@ edition = "2021"
clap = { version = "4.1.8", features = ["derive"] }
color-eyre = "0.6.2"
cosmwasm-std = "1.4.0"
ecies = { version = "0.2.3", default-features = false, features = ["pure"] }
k256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "alloc"] }
prost = "0.12"
rand = "0.8.5"
@ -17,7 +18,14 @@ tendermint-light-client = "0.34.0"
tonic = "0.11"
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
cw-proof = { path = "../../utils/cw-proof" }
cw-tee-mtcs = { git = "ssh://git@github.com/informalsystems/bisenzone-cw-mvp.git", branch = "hu55a1n1/11-use-quartz" }
mtcs = { git = "ssh://git@github.com/informalsystems/mtcs.git" }
quartz-cw = { git = "ssh://git@github.com/informalsystems/bisenzone-cw-mvp.git", branch = "hu55a1n1/11-use-quartz" }
quartz-proto = { path = "../../utils/quartz-proto" }
quartz-relayer = { path = "../../utils/quartz-relayer" }
quartz-tee-ra = { git = "ssh://git@github.com/informalsystems/bisenzone-cw-mvp.git", branch = "hu55a1n1/11-use-quartz" }
tm-stateless-verifier = { path = "../../utils/tm-stateless-verifier" }
[build-dependencies]
tonic-build = "0.11"

6
enclaves/quartz/build.rs Normal file
View file

@ -0,0 +1,6 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::configure()
.out_dir("src/prost")
.compile(&["proto/mtcs.proto"], &["proto"])?;
Ok(())
}

View file

@ -0,0 +1,15 @@
syntax = "proto3";
package mtcs;
service Clearing {
rpc Run (RunClearingRequest) returns (RunClearingResponse) {}
}
message RunClearingRequest {
string message = 1;
}
message RunClearingResponse {
string message = 1;
}

View file

@ -22,7 +22,7 @@ loader.env.MYAPP_DATA = { passthrough = true }
loader.argv = ["quartz-enclave",
"--chain-id", "testing",
"--trusted-height", "1",
"--trusted-hash", "A1D115BA3A5E9FCC12ED68A9D8669159E9085F6F96EC26619F5C7CEB4EE02869"]
"--trusted-hash", "2EF0E6F9BDDF5DEAA6FCD6492C3DB26D7C62BFFC01B538A958D04376E0B67185"]
fs.mounts = [
{ uri = "file:{{ gramine.runtimedir() }}", path = "/lib" },

View file

@ -15,9 +15,14 @@
mod attestor;
mod cli;
mod mtcs_server;
mod proto;
mod server;
use std::time::Duration;
use std::{
sync::{Arc, Mutex},
time::Duration,
};
use clap::Parser;
use quartz_cw::state::{Config, LightClientOpts};
@ -27,6 +32,8 @@ use tonic::transport::Server;
use crate::{
attestor::{Attestor, EpidAttestor},
cli::Cli,
mtcs_server::MtcsService,
proto::clearing_server::ClearingServer as MtcsServer,
server::CoreService,
};
@ -55,8 +62,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
light_client_opts,
);
let sk = Arc::new(Mutex::new(None));
Server::builder()
.add_service(CoreServer::new(CoreService::new(config, EpidAttestor)))
.add_service(CoreServer::new(CoreService::new(
config,
sk.clone(),
EpidAttestor,
)))
.add_service(MtcsServer::new(MtcsService::new(sk.clone(), EpidAttestor)))
.serve(args.rpc_addr)
.await?;

View file

@ -0,0 +1,87 @@
use std::{
collections::BTreeMap,
sync::{Arc, Mutex},
};
use cosmwasm_std::HexBinary;
use cw_tee_mtcs::{
msg::execute::SubmitSetoffsMsg,
state::{RawCipherText, RawHash},
};
use ecies::{decrypt, encrypt};
use k256::ecdsa::{SigningKey, VerifyingKey};
use mtcs::{
algo::mcmf::primal_dual::PrimalDual, impls::complex_id::ComplexIdMtcs,
obligation::SimpleObligation, prelude::DefaultMtcs, setoff::SimpleSetoff, Mtcs,
};
use tonic::{Request, Response, Result as TonicResult, Status};
use crate::{
attestor::Attestor,
proto::{clearing_server::Clearing, RunClearingRequest, RunClearingResponse},
};
#[derive(Clone, Debug)]
pub struct MtcsService<A> {
sk: Arc<Mutex<Option<SigningKey>>>,
_attestor: A,
}
impl<A> MtcsService<A>
where
A: Attestor,
{
pub fn new(sk: Arc<Mutex<Option<SigningKey>>>, _attestor: A) -> Self {
Self { sk, _attestor }
}
}
#[tonic::async_trait]
impl<A> Clearing for MtcsService<A>
where
A: Attestor + Send + Sync + 'static,
{
async fn run(
&self,
request: Request<RunClearingRequest>,
) -> TonicResult<Response<RunClearingResponse>> {
let message = request.into_inner().message;
let obligations_enc: BTreeMap<RawHash, RawCipherText> =
serde_json::from_str(&message).map_err(|e| Status::invalid_argument(e.to_string()))?;
let sk = self.sk.lock().unwrap();
let (digests, obligations): (Vec<RawHash>, Vec<SimpleObligation<HexBinary, i64>>) =
obligations_enc
.into_iter()
.map(|(digest, ciphertext)| {
let o = decrypt(&sk.as_ref().unwrap().to_bytes(), &ciphertext).unwrap();
(digest, serde_json::from_slice(&o).unwrap())
})
.unzip();
let mut mtcs = ComplexIdMtcs::wrapping(DefaultMtcs::new(PrimalDual::default()));
let setoffs: Vec<SimpleSetoff<HexBinary, i64>> = mtcs.run(obligations).unwrap();
let setoffs_enc: BTreeMap<RawHash, RawCipherText> = setoffs
.into_iter()
.zip(digests)
.flat_map(|(so, digest)| {
let debtor_pk = VerifyingKey::from_sec1_bytes(&so.debtor).unwrap();
let creditor_pk = VerifyingKey::from_sec1_bytes(&so.creditor).unwrap();
let so_ser = serde_json::to_string(&so).expect("infallible serializer");
let so_debtor = encrypt(&debtor_pk.to_sec1_bytes(), so_ser.as_bytes()).unwrap();
let so_creditor = encrypt(&creditor_pk.to_sec1_bytes(), so_ser.as_bytes()).unwrap();
[
(digest.clone(), so_debtor.into()),
(digest, so_creditor.into()),
]
})
.collect();
let message = serde_json::to_string(&SubmitSetoffsMsg { setoffs_enc }).unwrap();
Ok(Response::new(RunClearingResponse { message }))
}
}

View file

@ -0,0 +1,302 @@
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RunClearingRequest {
#[prost(string, tag = "1")]
pub message: ::prost::alloc::string::String,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RunClearingResponse {
#[prost(string, tag = "1")]
pub message: ::prost::alloc::string::String,
}
/// Generated client implementations.
pub mod clearing_client {
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
use tonic::codegen::*;
use tonic::codegen::http::Uri;
#[derive(Debug, Clone)]
pub struct ClearingClient<T> {
inner: tonic::client::Grpc<T>,
}
impl ClearingClient<tonic::transport::Channel> {
/// Attempt to create a new client by connecting to a given endpoint.
pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
where
D: TryInto<tonic::transport::Endpoint>,
D::Error: Into<StdError>,
{
let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
Ok(Self::new(conn))
}
}
impl<T> ClearingClient<T>
where
T: tonic::client::GrpcService<tonic::body::BoxBody>,
T::Error: Into<StdError>,
T::ResponseBody: Body<Data = Bytes> + Send + 'static,
<T::ResponseBody as Body>::Error: Into<StdError> + Send,
{
pub fn new(inner: T) -> Self {
let inner = tonic::client::Grpc::new(inner);
Self { inner }
}
pub fn with_origin(inner: T, origin: Uri) -> Self {
let inner = tonic::client::Grpc::with_origin(inner, origin);
Self { inner }
}
pub fn with_interceptor<F>(
inner: T,
interceptor: F,
) -> ClearingClient<InterceptedService<T, F>>
where
F: tonic::service::Interceptor,
T::ResponseBody: Default,
T: tonic::codegen::Service<
http::Request<tonic::body::BoxBody>,
Response = http::Response<
<T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody,
>,
>,
<T as tonic::codegen::Service<
http::Request<tonic::body::BoxBody>,
>>::Error: Into<StdError> + Send + Sync,
{
ClearingClient::new(InterceptedService::new(inner, interceptor))
}
/// Compress requests with the given encoding.
///
/// This requires the server to support it otherwise it might respond with an
/// error.
#[must_use]
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
self.inner = self.inner.send_compressed(encoding);
self
}
/// Enable decompressing responses.
#[must_use]
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
self.inner = self.inner.accept_compressed(encoding);
self
}
/// Limits the maximum size of a decoded message.
///
/// Default: `4MB`
#[must_use]
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
self.inner = self.inner.max_decoding_message_size(limit);
self
}
/// Limits the maximum size of an encoded message.
///
/// Default: `usize::MAX`
#[must_use]
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
self.inner = self.inner.max_encoding_message_size(limit);
self
}
pub async fn run(
&mut self,
request: impl tonic::IntoRequest<super::RunClearingRequest>,
) -> std::result::Result<
tonic::Response<super::RunClearingResponse>,
tonic::Status,
> {
self.inner
.ready()
.await
.map_err(|e| {
tonic::Status::new(
tonic::Code::Unknown,
format!("Service was not ready: {}", e.into()),
)
})?;
let codec = tonic::codec::ProstCodec::default();
let path = http::uri::PathAndQuery::from_static("/mtcs.Clearing/Run");
let mut req = request.into_request();
req.extensions_mut().insert(GrpcMethod::new("mtcs.Clearing", "Run"));
self.inner.unary(req, path, codec).await
}
}
}
/// Generated server implementations.
pub mod clearing_server {
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
use tonic::codegen::*;
/// Generated trait containing gRPC methods that should be implemented for use with ClearingServer.
#[async_trait]
pub trait Clearing: Send + Sync + 'static {
async fn run(
&self,
request: tonic::Request<super::RunClearingRequest>,
) -> std::result::Result<
tonic::Response<super::RunClearingResponse>,
tonic::Status,
>;
}
#[derive(Debug)]
pub struct ClearingServer<T: Clearing> {
inner: _Inner<T>,
accept_compression_encodings: EnabledCompressionEncodings,
send_compression_encodings: EnabledCompressionEncodings,
max_decoding_message_size: Option<usize>,
max_encoding_message_size: Option<usize>,
}
struct _Inner<T>(Arc<T>);
impl<T: Clearing> ClearingServer<T> {
pub fn new(inner: T) -> Self {
Self::from_arc(Arc::new(inner))
}
pub fn from_arc(inner: Arc<T>) -> Self {
let inner = _Inner(inner);
Self {
inner,
accept_compression_encodings: Default::default(),
send_compression_encodings: Default::default(),
max_decoding_message_size: None,
max_encoding_message_size: None,
}
}
pub fn with_interceptor<F>(
inner: T,
interceptor: F,
) -> InterceptedService<Self, F>
where
F: tonic::service::Interceptor,
{
InterceptedService::new(Self::new(inner), interceptor)
}
/// Enable decompressing requests with the given encoding.
#[must_use]
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
self.accept_compression_encodings.enable(encoding);
self
}
/// Compress responses with the given encoding, if the client supports it.
#[must_use]
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
self.send_compression_encodings.enable(encoding);
self
}
/// Limits the maximum size of a decoded message.
///
/// Default: `4MB`
#[must_use]
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
self.max_decoding_message_size = Some(limit);
self
}
/// Limits the maximum size of an encoded message.
///
/// Default: `usize::MAX`
#[must_use]
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
self.max_encoding_message_size = Some(limit);
self
}
}
impl<T, B> tonic::codegen::Service<http::Request<B>> for ClearingServer<T>
where
T: Clearing,
B: Body + Send + 'static,
B::Error: Into<StdError> + Send + 'static,
{
type Response = http::Response<tonic::body::BoxBody>;
type Error = std::convert::Infallible;
type Future = BoxFuture<Self::Response, Self::Error>;
fn poll_ready(
&mut self,
_cx: &mut Context<'_>,
) -> Poll<std::result::Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: http::Request<B>) -> Self::Future {
let inner = self.inner.clone();
match req.uri().path() {
"/mtcs.Clearing/Run" => {
#[allow(non_camel_case_types)]
struct RunSvc<T: Clearing>(pub Arc<T>);
impl<
T: Clearing,
> tonic::server::UnaryService<super::RunClearingRequest>
for RunSvc<T> {
type Response = super::RunClearingResponse;
type Future = BoxFuture<
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call(
&mut self,
request: tonic::Request<super::RunClearingRequest>,
) -> Self::Future {
let inner = Arc::clone(&self.0);
let fut = async move {
<T as Clearing>::run(&inner, request).await
};
Box::pin(fut)
}
}
let accept_compression_encodings = self.accept_compression_encodings;
let send_compression_encodings = self.send_compression_encodings;
let max_decoding_message_size = self.max_decoding_message_size;
let max_encoding_message_size = self.max_encoding_message_size;
let inner = self.inner.clone();
let fut = async move {
let inner = inner.0;
let method = RunSvc(inner);
let codec = tonic::codec::ProstCodec::default();
let mut grpc = tonic::server::Grpc::new(codec)
.apply_compression_config(
accept_compression_encodings,
send_compression_encodings,
)
.apply_max_message_size_config(
max_decoding_message_size,
max_encoding_message_size,
);
let res = grpc.unary(method, req).await;
Ok(res)
};
Box::pin(fut)
}
_ => {
Box::pin(async move {
Ok(
http::Response::builder()
.status(200)
.header("grpc-status", "12")
.header("content-type", "application/grpc")
.body(empty_body())
.unwrap(),
)
})
}
}
}
}
impl<T: Clearing> Clone for ClearingServer<T> {
fn clone(&self) -> Self {
let inner = self.inner.clone();
Self {
inner,
accept_compression_encodings: self.accept_compression_encodings,
send_compression_encodings: self.send_compression_encodings,
max_decoding_message_size: self.max_decoding_message_size,
max_encoding_message_size: self.max_encoding_message_size,
}
}
}
impl<T: Clearing> Clone for _Inner<T> {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
impl<T: std::fmt::Debug> std::fmt::Debug for _Inner<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.0)
}
}
impl<T: Clearing> tonic::server::NamedService for ClearingServer<T> {
const NAME: &'static str = "mtcs.Clearing";
}
}

View file

@ -0,0 +1,3 @@
#![allow(clippy::unwrap_used, unused_qualifications)]
include!(concat!("prost/", "mtcs.rs"));

View file

@ -1,12 +1,22 @@
use std::sync::{Arc, Mutex};
use std::{
sync::{Arc, Mutex},
time::Duration,
};
use cw_proof::{
error::ProofError,
proof::{
cw::{CwProof, RawCwProof},
Proof,
},
};
use k256::ecdsa::SigningKey;
use quartz_cw::{
msg::{
execute::{session_create::SessionCreate, session_set_pub_key::SessionSetPubKey},
instantiate::CoreInstantiate,
},
state::{Config, Nonce},
state::{Config, Nonce, Session},
};
use quartz_proto::quartz::{
core_server::Core, InstantiateRequest as RawInstantiateRequest,
@ -17,16 +27,21 @@ use quartz_proto::quartz::{
};
use quartz_relayer::types::{InstantiateResponse, SessionCreateResponse, SessionSetPubKeyResponse};
use rand::Rng;
use tonic::{Request, Response, Status};
use serde::{Deserialize, Serialize};
use tendermint_light_client::{
light_client::Options,
types::{LightBlock, TrustThreshold},
};
use tm_stateless_verifier::make_provider;
use tonic::{Request, Response, Result as TonicResult, Status};
use crate::attestor::Attestor;
type TonicResult<T> = Result<T, Status>;
#[derive(Clone, Debug)]
pub struct CoreService<A> {
config: Config,
nonce: Arc<Mutex<Nonce>>,
sk: Arc<Mutex<Option<SigningKey>>>,
attestor: A,
}
@ -34,10 +49,11 @@ impl<A> CoreService<A>
where
A: Attestor,
{
pub fn new(config: Config, attestor: A) -> Self {
pub fn new(config: Config, sk: Arc<Mutex<Option<SigningKey>>>, attestor: A) -> Self {
Self {
config,
nonce: Arc::new(Mutex::new([0u8; 32])),
sk,
attestor,
}
}
@ -66,6 +82,7 @@ where
&self,
_request: Request<RawSessionCreateRequest>,
) -> TonicResult<Response<RawSessionCreateResponse>> {
// FIXME(hu55a1n1) - disallow calling more than once
let mut nonce = self.nonce.lock().unwrap();
*nonce = rand::thread_rng().gen::<Nonce>();
@ -82,10 +99,69 @@ where
async fn session_set_pub_key(
&self,
_request: Request<RawSessionSetPubKeyRequest>,
request: Request<RawSessionSetPubKeyRequest>,
) -> TonicResult<Response<RawSessionSetPubKeyResponse>> {
// FIXME(hu55a1n1) - disallow calling more than once
let proof: ProofOfPublication = serde_json::from_str(&request.into_inner().message)
.map_err(|e| Status::invalid_argument(e.to_string()))?;
let config_trust_threshold = self.config.light_client_opts().trust_threshold();
let trust_threshold =
TrustThreshold::new(config_trust_threshold.0, config_trust_threshold.1).unwrap();
let config_trusting_period = self.config.light_client_opts().trusting_period();
let trusting_period = Duration::from_secs(config_trusting_period);
let config_clock_drift = self.config.light_client_opts().max_clock_drift();
let clock_drift = Duration::from_secs(config_clock_drift);
let options = Options {
trust_threshold,
trusting_period,
clock_drift,
};
let target_height = proof.light_client_proof.last().unwrap().height();
let primary_block = make_provider(
self.config.light_client_opts().chain_id(),
self.config
.light_client_opts()
.trusted_height()
.try_into()
.unwrap(),
self.config
.light_client_opts()
.trusted_hash()
.to_vec()
.try_into()
.unwrap(),
proof.light_client_proof,
options,
)
.and_then(|mut primary| primary.verify_to_height(target_height))
.map_err(|e| Status::internal(e.to_string()))?;
let proof = CwProof::from(proof.merkle_proof);
proof
.verify(
primary_block
.signed_header
.header
.app_hash
.as_bytes()
.to_vec(),
)
.map_err(|e: ProofError| Status::internal(e.to_string()))?;
let session: Session = serde_json::from_slice(&proof.value).unwrap();
let nonce = self.nonce.lock().unwrap();
if session.nonce() != *nonce {
return Err(Status::unauthenticated("nonce mismatch"));
}
let sk = SigningKey::random(&mut rand::thread_rng());
*self.sk.lock().unwrap() = Some(sk.clone());
let pk = sk.verifying_key();
let session_set_pub_key_msg = SessionSetPubKey::new(*nonce, *pk);
@ -99,3 +175,9 @@ where
Ok(Response::new(response.into()))
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ProofOfPublication {
light_client_proof: Vec<LightBlock>,
merkle_proof: RawCwProof,
}

3
utils/mtcs-intent/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
*.pk
*.sk

View file

@ -0,0 +1,17 @@
[package]
name = "mtcs-intent"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = { version = "4.0.32", features = ["derive"] }
cosmwasm-std = "1.4.0"
ecies = "0.2.6"
hex = "0.4.3"
k256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "alloc", "serde"] }
rand = "0.8.5"
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.114"
sha2 = "0.10.8"

View file

@ -0,0 +1,183 @@
#![forbid(unsafe_code)]
#![warn(
clippy::checked_conversions,
clippy::panic,
clippy::panic_in_result_fn,
clippy::unwrap_used,
trivial_casts,
trivial_numeric_casts,
rust_2018_idioms,
unused_lifetimes,
unused_import_braces,
unused_qualifications
)]
use std::{
error::Error,
fs::{read_to_string, File},
io::Write,
path::PathBuf,
};
use clap::{Parser, Subcommand};
use cosmwasm_std::HexBinary;
use ecies::{decrypt, encrypt};
use k256::{
ecdsa::{SigningKey, VerifyingKey},
elliptic_curve::generic_array::GenericArray,
};
use rand::Rng;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
#[derive(Debug, Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
#[command(subcommand)]
pub command: Command,
}
#[derive(Debug, Subcommand)]
#[allow(clippy::large_enum_variant)]
enum Command {
KeyGen {
#[clap(long, default_value = "user.pk")]
pk_file: PathBuf,
#[clap(long, default_value = "user.sk")]
sk_file: PathBuf,
},
EncryptObligation {
#[clap(long, value_parser = parse_obligation_json)]
obligation: Obligation,
#[clap(long, default_value = "epoch.pk")]
pk_file: PathBuf,
},
DecryptSetoff {
#[clap(long, value_parser = parse_hex)]
setoff: Vec<u8>,
#[clap(long)]
sk_file: PathBuf,
},
}
fn parse_obligation_json(s: &str) -> Result<Obligation, String> {
let raw_obligation: RawObligation = serde_json::from_str(s).map_err(|e| e.to_string())?;
raw_obligation.try_into()
}
fn parse_hex(s: &str) -> Result<Vec<u8>, String> {
Ok(hex::decode(s).unwrap())
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct RawObligation {
debtor: HexBinary,
creditor: HexBinary,
amount: u64,
#[serde(default)]
salt: HexBinary,
}
#[derive(Clone, Debug)]
struct Obligation {
debtor: VerifyingKey,
creditor: VerifyingKey,
amount: u64,
salt: [u8; 64],
}
impl TryFrom<RawObligation> for Obligation {
type Error = String;
fn try_from(raw_obligation: RawObligation) -> Result<Self, Self::Error> {
let mut salt = [0u8; 64];
rand::thread_rng().fill(&mut salt[..]);
Ok(Self {
debtor: VerifyingKey::from_sec1_bytes(raw_obligation.debtor.as_slice())
.map_err(|e| e.to_string())?,
creditor: VerifyingKey::from_sec1_bytes(raw_obligation.creditor.as_slice())
.map_err(|e| e.to_string())?,
amount: raw_obligation.amount,
salt,
})
}
}
impl From<Obligation> for RawObligation {
fn from(obligation: Obligation) -> Self {
Self {
debtor: obligation.debtor.to_sec1_bytes().into_vec().into(),
creditor: obligation.creditor.to_sec1_bytes().into_vec().into(),
amount: obligation.amount,
salt: obligation.salt.into(),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct EncryptedObligation {
ciphertext: HexBinary,
digest: HexBinary,
}
fn main() -> Result<(), Box<dyn Error>> {
let args = Cli::parse();
match args.command {
Command::KeyGen { pk_file, sk_file } => {
let sk = SigningKey::random(&mut rand::thread_rng());
let pk = sk.verifying_key();
let mut sk_file = File::create(sk_file)?;
let sk = hex::encode(sk.to_bytes());
sk_file.write_all(sk.as_bytes())?;
let mut pk_file = File::create(pk_file)?;
let pk = hex::encode(pk.to_sec1_bytes());
pk_file.write_all(pk.as_bytes())?;
}
Command::EncryptObligation {
obligation,
pk_file,
} => {
let epoch_pk = {
let pk_str = read_to_string(pk_file)?;
hex::decode(pk_str)?
};
let obligation_ser = serde_json::to_string(&RawObligation::from(obligation))
.expect("infallible serializer");
let ciphertext =
encrypt(&epoch_pk, obligation_ser.as_bytes()).map_err(|e| e.to_string())?;
let digest: [u8; 32] = {
let mut hasher = Sha256::new();
hasher.update(obligation_ser);
hasher.finalize().into()
};
let obligation_enc = EncryptedObligation {
ciphertext: ciphertext.into(),
digest: digest.into(),
};
println!(
"{}",
serde_json::to_string(&obligation_enc).expect("infallible serializer")
);
}
Command::DecryptSetoff { setoff, sk_file } => {
let sk = {
let sk_str = read_to_string(sk_file)?;
let sk = hex::decode(sk_str).expect("");
SigningKey::from_bytes(GenericArray::from_slice(&sk))?
};
let key_share = decrypt(&sk.to_bytes(), &setoff).unwrap();
serde_json::from_slice(&key_share)?;
}
}
Ok(())
}

View file

@ -20,7 +20,9 @@ message SessionCreateResponse {
string message = 1;
}
message SessionSetPubKeyRequest {}
message SessionSetPubKeyRequest {
string message = 1;
}
message SessionSetPubKeyResponse {
string message = 1;

View file

@ -18,7 +18,10 @@ pub struct SessionCreateResponse {
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SessionSetPubKeyRequest {}
pub struct SessionSetPubKeyRequest {
#[prost(string, tag = "1")]
pub message: ::prost::alloc::string::String,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SessionSetPubKeyResponse {

View file

@ -0,0 +1,54 @@
#!/bin/bash
set -eo pipefail
usage() {
echo "Usage: $0 <REQUEST> <REQUEST_MSG>"
echo " <REQUEST>: Instantiate | SessionCreate | SessionSetPubKey"
exit 1
}
IAS_API_KEY="669244b3e6364b5888289a11d2a1726d"
RA_CLIENT_SPID="51CAF5A48B450D624AEFE3286D314894"
QUOTE_FILE="/tmp/test.quote"
REPORT_FILE="/tmp/datareport"
REPORT_SIG_FILE="/tmp/datareportsig"
REQUEST="$1"
REQUEST_MSG=${2:-"{}"}
# clear tmp files from previous runs
rm -f "$QUOTE_FILE" "$REPORT_FILE" "$REPORT_SIG_FILE"
# query the gRPC quartz enclave service
ATTESTED_MSG=$(grpcurl -plaintext -import-path ../../utils/quartz-proto/proto/ -proto quartz.proto -d "$REQUEST_MSG" '127.0.0.1:11090' quartz.Core/"$REQUEST" | jq -c '.message | fromjson')
# parse out the quote and the message
QUOTE=$(echo "$ATTESTED_MSG" | jq -c '.quote')
MSG=$(echo "$ATTESTED_MSG" | jq 'del(.quote)')
# request the IAS report for EPID attestations
echo -n "$QUOTE" | xxd -r -p - > "$QUOTE_FILE"
gramine-sgx-ias-request report -g "$RA_CLIENT_SPID" -k "$IAS_API_KEY" -q "$QUOTE_FILE" -r "$REPORT_FILE" -s "$REPORT_SIG_FILE" > /dev/null 2>&1
REPORT=$(cat "$REPORT_FILE")
REPORTSIG=$(cat "$REPORT_SIG_FILE" | tr -d '\r')
#echo "$QUOTE"
#echo "$REPORT"
#echo "$REPORTSIG"
case "$REQUEST" in
"Instantiate")
jq -nc --argjson msg "$MSG" --argjson "attestation" \
"$(jq -nc --argjson report "$(jq -nc --argjson report "$REPORT" --arg reportsig "$REPORTSIG" '$ARGS.named')" '$ARGS.named')" \
'$ARGS.named' ;;
"SessionCreate" | "SessionSetPubKey")
REQUEST_KEY=$(echo "$REQUEST" | sed 's/\([A-Z]\)/_\L\1/g;s/^_//')
jq -nc --argjson quartz "$(jq -nc --argjson "$REQUEST_KEY" "$(jq -nc --argjson msg "$MSG" --argjson attestation \
"$(jq -nc --argjson report "$(jq -nc --argjson report "$REPORT" --arg reportsig "$REPORTSIG" '$ARGS.named')" '$ARGS.named')" \
'$ARGS.named')" '$ARGS.named')" '$ARGS.named' ;;
*)
usage ;;
esac

View file

@ -6,12 +6,12 @@ block height and trusted height/hash.
## Usage
```bash
cargo run -- --chain-id osmosis-1 \
cargo run -- --chain-id testing \
--primary "http://127.0.0.1:26657" \
--witnesses "http://127.0.0.1:26657" \
--trusted-height 1 \
--trusted-hash "798E237C6FDF39EDA8BA7AB8E8F5DC71F24BC7138BE31882338022F8F88086EE" \
--trusted-hash "2EF0E6F9BDDF5DEAA6FCD6492C3DB26D7C62BFFC01B538A958D04376E0B67185" \
--contract-address "wasm14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s0phg4d" \
--storage-key "requests" \
--storage-key "quartz_session" \
--trace-file light-client-proof.json
```

File diff suppressed because one or more lines are too long