feat: quartz-common and merging quartz-cli business logic from cycles-protocol (#123)
This commit is contained in:
parent
b2fba137c4
commit
63aa30db6b
60 changed files with 8382 additions and 1027 deletions
3
.github/workflows/cosmwasm-basic.yml
vendored
3
.github/workflows/cosmwasm-basic.yml
vendored
|
@ -37,6 +37,9 @@ jobs:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install protobuf-compiler
|
||||||
|
run: sudo apt-get install -y protobuf-compiler
|
||||||
|
|
||||||
- name: Install wasm32-unknown-unknown toolchain
|
- name: Install wasm32-unknown-unknown toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
|
|
2
.github/workflows/cosmwasm-release.yml
vendored
2
.github/workflows/cosmwasm-release.yml
vendored
|
@ -10,6 +10,8 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
- name: Install protobuf-compiler
|
||||||
|
run: sudo apt-get install -y protobuf-compiler
|
||||||
- name: Install cargo-run-script
|
- name: Install cargo-run-script
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -9,3 +9,5 @@
|
||||||
.idea/
|
.idea/
|
||||||
target/
|
target/
|
||||||
artifacts/
|
artifacts/
|
||||||
|
.vscode/
|
||||||
|
.DS_Store
|
613
Cargo.lock
generated
613
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
11
Cargo.toml
11
Cargo.toml
|
@ -2,10 +2,12 @@
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"apps/mtcs/enclave",
|
"apps/mtcs/enclave",
|
||||||
|
"apps/mtcs/scripts",
|
||||||
"apps/transfers/enclave",
|
"apps/transfers/enclave",
|
||||||
"cli",
|
"cli",
|
||||||
"core/light-client-proofs/*",
|
"core/light-client-proofs/*",
|
||||||
"core/quartz",
|
"core/quartz",
|
||||||
|
"core/quartz-common",
|
||||||
"cosmwasm/packages/*",
|
"cosmwasm/packages/*",
|
||||||
"utils/*",
|
"utils/*",
|
||||||
]
|
]
|
||||||
|
@ -76,15 +78,20 @@ mc-attestation-verifier = { git = "https://github.com/informalsystems/attestatio
|
||||||
|
|
||||||
# quartz
|
# quartz
|
||||||
cw-proof = { path = "core/light-client-proofs/cw-proof", default-features = false }
|
cw-proof = { path = "core/light-client-proofs/cw-proof", default-features = false }
|
||||||
cw-tee-mtcs = { path = "apps/mtcs/contracts/cw-tee-mtcs", default-features = false }
|
|
||||||
cycles-sync = { path = "utils/cycles-sync", default-features = false }
|
cycles-sync = { path = "utils/cycles-sync", default-features = false }
|
||||||
mtcs = { git = "ssh://git@github.com/informalsystems/mtcs.git", default-features = false }
|
quartz-common = { path = "core/quartz-common" }
|
||||||
quartz-cw = { path = "cosmwasm/packages/quartz-cw", default-features = false }
|
quartz-cw = { path = "cosmwasm/packages/quartz-cw", default-features = false }
|
||||||
quartz-enclave = { path = "core/quartz", default-features = false }
|
quartz-enclave = { path = "core/quartz", default-features = false }
|
||||||
quartz-proto = { path = "core/quartz-proto", default-features = false }
|
quartz-proto = { path = "core/quartz-proto", default-features = false }
|
||||||
quartz-relayer = { path = "relayer", default-features = false }
|
quartz-relayer = { path = "relayer", default-features = false }
|
||||||
quartz-tee-ra = { path = "cosmwasm/packages/quartz-tee-ra", default-features = false }
|
quartz-tee-ra = { path = "cosmwasm/packages/quartz-tee-ra", default-features = false }
|
||||||
|
tm-prover = { path = "utils/tm-prover", default-features = false }
|
||||||
tm-stateless-verifier = { path = "core/light-client-proofs/tm-stateless-verifier", default-features = false }
|
tm-stateless-verifier = { path = "core/light-client-proofs/tm-stateless-verifier", default-features = false }
|
||||||
|
|
||||||
|
# quartz apps
|
||||||
|
cw-tee-mtcs = { path = "apps/mtcs/contracts/cw-tee-mtcs", default-features = false }
|
||||||
|
mtcs = { git = "ssh://git@github.com/informalsystems/mtcs.git", default-features = false }
|
||||||
|
mtcs-enclave = { path = "apps/mtcs/enclave" }
|
||||||
transfers-contract = { path = "apps/transfers/contracts", default-features = false }
|
transfers-contract = { path = "apps/transfers/contracts", default-features = false }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|
147
apps/mtcs/contracts/cw-tee-mtcs/Cargo.lock
generated
147
apps/mtcs/contracts/cw-tee-mtcs/Cargo.lock
generated
|
@ -43,7 +43,7 @@ checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.70",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.70",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -96,9 +96,9 @@ checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.5.0"
|
version = "2.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
|
@ -144,9 +144,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.103"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2755ff20a1d93490d26ba33a6f092a38a508398a5320df5d4b3014fcccce9410"
|
checksum = "eaff6f8ce506b9773fa786672d63fc7a191ffea1be33f72bbd4aeacefca9ffc8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
|
@ -317,7 +317,7 @@ dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"hex",
|
"hex",
|
||||||
"k256",
|
"k256",
|
||||||
"quartz-cw",
|
"quartz-common",
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.8",
|
||||||
|
@ -384,9 +384,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.20.9"
|
version = "0.20.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1"
|
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"darling_macro",
|
"darling_macro",
|
||||||
|
@ -394,27 +394,27 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_core"
|
name = "darling_core"
|
||||||
version = "0.20.9"
|
version = "0.20.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120"
|
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fnv",
|
"fnv",
|
||||||
"ident_case",
|
"ident_case",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim",
|
"strsim",
|
||||||
"syn 2.0.66",
|
"syn 2.0.70",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_macro"
|
name = "darling_macro"
|
||||||
version = "0.20.9"
|
version = "0.20.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
|
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.70",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -452,13 +452,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "der_derive"
|
name = "der_derive"
|
||||||
version = "0.7.2"
|
version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049"
|
checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.70",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -504,13 +504,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.4"
|
version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.70",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -550,9 +550,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.12.0"
|
version = "1.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "elliptic-curve"
|
name = "elliptic-curve"
|
||||||
|
@ -810,9 +810,9 @@ source = "git+https://github.com/informalsystems/sgx#f25807776cbe10901f53d23fca5
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.2"
|
version = "2.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
|
@ -832,9 +832,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-bigint"
|
name = "num-bigint"
|
||||||
version = "0.4.5"
|
version = "0.4.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7"
|
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
@ -933,9 +933,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.84"
|
version = "1.0.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6"
|
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -960,7 +960,14 @@ dependencies = [
|
||||||
"itertools",
|
"itertools",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.70",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quartz-common"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"quartz-cw",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1086,7 +1093,7 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde_derive_internals",
|
"serde_derive_internals",
|
||||||
"syn 2.0.66",
|
"syn 2.0.70",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1111,9 +1118,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.203"
|
version = "1.0.204"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
@ -1129,13 +1136,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.203"
|
version = "1.0.204"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.70",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1146,14 +1153,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.70",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.117"
|
version = "1.0.120"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
|
@ -1162,9 +1169,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_with"
|
name = "serde_with"
|
||||||
version = "3.8.1"
|
version = "3.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20"
|
checksum = "e73139bc5ec2d45e6c5fd85be5a46949c1c39a4c18e56915f5eb4c12f975e377"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
|
@ -1173,14 +1180,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_with_macros"
|
name = "serde_with_macros"
|
||||||
version = "3.8.1"
|
version = "3.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2"
|
checksum = "b80d3d6b56b64335c0180e5ffde23b3c5e08c14c585b51a15bd0e95393f46703"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.70",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1247,9 +1254,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.5.0"
|
version = "2.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
|
@ -1264,9 +1271,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.66"
|
version = "2.0.70"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
|
checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1281,7 +1288,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.70",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1301,7 +1308,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.70",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1386,7 +1393,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.70",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1408,7 +1415,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.70",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
@ -1430,9 +1437,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc",
|
||||||
|
@ -1446,51 +1453,51 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnullvm"
|
name = "windows_i686_gnullvm"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "x509-cert"
|
name = "x509-cert"
|
||||||
|
|
|
@ -20,7 +20,8 @@ incremental = false
|
||||||
overflow-checks = true
|
overflow-checks = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
mock-sgx = ["quartz-cw/mock-sgx"]
|
library = []
|
||||||
|
mock-sgx = ["quartz-common/mock-sgx-cw"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# external
|
# external
|
||||||
|
@ -40,7 +41,7 @@ cw20 = "2.0.0"
|
||||||
cw2 = "2.0.0"
|
cw2 = "2.0.0"
|
||||||
|
|
||||||
# quartz
|
# quartz
|
||||||
quartz-cw = { path = "../../../../cosmwasm/packages/quartz-cw" }
|
quartz-common = { path = "../../../../core/quartz-common/", features = ["contract"]}
|
||||||
|
|
||||||
# patch indirect deps
|
# patch indirect deps
|
||||||
getrandom = { version = "0.2.15", features = ["js"] }
|
getrandom = { version = "0.2.15", features = ["js"] }
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
use cosmwasm_std::{
|
use cosmwasm_std::{
|
||||||
entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult,
|
entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult,
|
||||||
Uint128, Uint64,
|
Uint64,
|
||||||
};
|
};
|
||||||
use cw2::set_contract_version;
|
use cw2::set_contract_version;
|
||||||
use cw20_base::{
|
use cw20_base::contract::query_balance as cw20_query_balance;
|
||||||
contract::query_balance as cw20_query_balance,
|
use quartz_common::contract::{handler::RawHandler, state::EPOCH_COUNTER};
|
||||||
state::{MinterData, TokenInfo, TOKEN_INFO},
|
|
||||||
};
|
|
||||||
use quartz_cw::{handler::RawHandler, state::EPOCH_COUNTER};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::ContractError,
|
error::ContractError,
|
||||||
|
@ -19,7 +16,7 @@ use crate::{
|
||||||
ExecuteMsg, InstantiateMsg, QueryMsg,
|
ExecuteMsg, InstantiateMsg, QueryMsg,
|
||||||
},
|
},
|
||||||
state::{
|
state::{
|
||||||
current_epoch_key, LiquiditySourcesItem, ObligationsItem, State, LIQUIDITY_SOURCES_KEY,
|
current_epoch_key, ObligationsItem, State, LIQUIDITY_SOURCES, LIQUIDITY_SOURCES_KEY,
|
||||||
OBLIGATIONS_KEY, STATE,
|
OBLIGATIONS_KEY, STATE,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -41,6 +38,7 @@ pub fn instantiate(
|
||||||
let state = State {
|
let state = State {
|
||||||
owner: info.sender.to_string(),
|
owner: info.sender.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
||||||
STATE.save(deps.storage, &state)?;
|
STATE.save(deps.storage, &state)?;
|
||||||
|
|
||||||
|
@ -50,22 +48,9 @@ pub fn instantiate(
|
||||||
ObligationsItem::new_dyn(current_epoch_key(OBLIGATIONS_KEY, deps.storage)?)
|
ObligationsItem::new_dyn(current_epoch_key(OBLIGATIONS_KEY, deps.storage)?)
|
||||||
.save(deps.storage, &Default::default())?;
|
.save(deps.storage, &Default::default())?;
|
||||||
|
|
||||||
LiquiditySourcesItem::new_dyn(current_epoch_key(LIQUIDITY_SOURCES_KEY, deps.storage)?)
|
// TODO: this can be removed. We don't need to instantiate liquidity sources, users will do so when submitting obligations
|
||||||
.save(deps.storage, &Default::default())?;
|
let epoch = current_epoch_key(LIQUIDITY_SOURCES_KEY, deps.storage)?;
|
||||||
|
LIQUIDITY_SOURCES.save(deps.storage, &epoch, &vec![])?;
|
||||||
// store token info using cw20-base format
|
|
||||||
let data = TokenInfo {
|
|
||||||
name: "USD".to_string(),
|
|
||||||
symbol: "!$".to_string(),
|
|
||||||
decimals: 0,
|
|
||||||
total_supply: Uint128::zero(),
|
|
||||||
// set self as minter, so we can properly execute mint and burn
|
|
||||||
mint: Some(MinterData {
|
|
||||||
minter: env.contract.address.clone(),
|
|
||||||
cap: None,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
TOKEN_INFO.save(deps.storage, &data)?;
|
|
||||||
|
|
||||||
Ok(Response::new()
|
Ok(Response::new()
|
||||||
.add_attribute("method", "instantiate")
|
.add_attribute("method", "instantiate")
|
||||||
|
@ -97,19 +82,21 @@ pub fn execute(
|
||||||
for o in obligations {
|
for o in obligations {
|
||||||
execute::submit_obligation(deps.branch(), o.ciphertext, o.digest)?;
|
execute::submit_obligation(deps.branch(), o.ciphertext, o.digest)?;
|
||||||
}
|
}
|
||||||
execute::set_liquidity_sources(deps, liquidity_sources)?;
|
|
||||||
|
execute::append_liquidity_sources(deps, liquidity_sources)?;
|
||||||
Ok(Response::new())
|
Ok(Response::new())
|
||||||
}
|
}
|
||||||
ExecuteMsg::SubmitSetoffs(attested_msg) => {
|
ExecuteMsg::SubmitSetoffs(attested_msg) => {
|
||||||
let _ = attested_msg
|
// let _ = attested_msg
|
||||||
.clone()
|
// .clone()
|
||||||
.handle_raw(deps.branch(), &env, &info)?;
|
// .handle_raw(deps.branch(), &env, &info)?;
|
||||||
let SubmitSetoffsMsg { setoffs_enc } = attested_msg.msg.0;
|
let SubmitSetoffsMsg { setoffs_enc } = attested_msg.msg.0;
|
||||||
execute::submit_setoffs(deps, env, setoffs_enc)
|
execute::submit_setoffs(deps, env, setoffs_enc)
|
||||||
}
|
}
|
||||||
ExecuteMsg::InitClearing => execute::init_clearing(deps),
|
ExecuteMsg::InitClearing => execute::init_clearing(deps),
|
||||||
ExecuteMsg::SetLiquiditySources(SetLiquiditySourcesMsg { liquidity_sources }) => {
|
ExecuteMsg::SetLiquiditySources(SetLiquiditySourcesMsg { liquidity_sources }) => {
|
||||||
execute::set_liquidity_sources(deps, liquidity_sources)
|
execute::append_liquidity_sources(deps, liquidity_sources)?;
|
||||||
|
Ok(Response::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,15 +104,21 @@ pub fn execute(
|
||||||
pub mod execute {
|
pub mod execute {
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use cosmwasm_std::{DepsMut, Env, HexBinary, MessageInfo, Response, StdResult, Uint64};
|
use cosmwasm_std::{
|
||||||
use cw20_base::contract::{execute_burn, execute_mint};
|
to_json_binary, Addr, DepsMut, Env, HexBinary, MessageInfo, Response, StdResult, Storage,
|
||||||
use k256::ecdsa::VerifyingKey;
|
SubMsg, Uint64, WasmMsg,
|
||||||
use quartz_cw::state::{Hash, EPOCH_COUNTER};
|
};
|
||||||
|
use cw20_base::contract::execute_mint;
|
||||||
|
use quartz_common::contract::state::{Hash, EPOCH_COUNTER};
|
||||||
|
|
||||||
|
// use mtcs_overdraft::msg::ExecuteMsg as OverdraftExecuteMsg;
|
||||||
|
use crate::msg::OverdraftExecuteMsg; // TODO: change when dependency issue fiexed
|
||||||
use crate::{
|
use crate::{
|
||||||
|
msg::execute::EscrowExecuteMsg,
|
||||||
state::{
|
state::{
|
||||||
current_epoch_key, previous_epoch_key, LiquiditySourcesItem, ObligationsItem, RawHash,
|
current_epoch_key, previous_epoch_key, LiquiditySource, LiquiditySourceType,
|
||||||
SetoffsItem, SettleOff, LIQUIDITY_SOURCES_KEY, OBLIGATIONS_KEY, SETOFFS_KEY,
|
ObligationsItem, RawHash, SetoffsItem, SettleOff, Transfer, LIQUIDITY_SOURCES,
|
||||||
|
LIQUIDITY_SOURCES_KEY, OBLIGATIONS_KEY, SETOFFS_KEY,
|
||||||
},
|
},
|
||||||
ContractError,
|
ContractError,
|
||||||
};
|
};
|
||||||
|
@ -160,15 +153,16 @@ pub mod execute {
|
||||||
let _: Hash = digest.to_array()?;
|
let _: Hash = digest.to_array()?;
|
||||||
|
|
||||||
// store the `(digest, ciphertext)` tuple
|
// store the `(digest, ciphertext)` tuple
|
||||||
ObligationsItem::new_dyn(current_epoch_key(OBLIGATIONS_KEY, deps.storage)?).update(
|
let obligs_key =
|
||||||
deps.storage,
|
ObligationsItem::new_dyn(current_epoch_key(OBLIGATIONS_KEY, deps.storage)?);
|
||||||
|mut obligations| {
|
|
||||||
if let Some(_duplicate) = obligations.insert(digest.clone(), ciphertext.clone()) {
|
let mut epoch_obligation = obligs_key.may_load(deps.storage)?.unwrap_or_default();
|
||||||
return Err(ContractError::DuplicateEntry);
|
|
||||||
}
|
if let Some(_duplicate) = epoch_obligation.insert(digest.clone(), ciphertext.clone()) {
|
||||||
Ok(obligations)
|
return Err(ContractError::DuplicateEntry);
|
||||||
},
|
}
|
||||||
)?;
|
|
||||||
|
obligs_key.save(deps.storage, &epoch_obligation)?;
|
||||||
|
|
||||||
Ok(Response::new()
|
Ok(Response::new()
|
||||||
.add_attribute("action", "submit_obligation")
|
.add_attribute("action", "submit_obligation")
|
||||||
|
@ -176,66 +170,162 @@ pub mod execute {
|
||||||
.add_attribute("ciphertext", ciphertext.to_string()))
|
.add_attribute("ciphertext", ciphertext.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_liquidity_sources(
|
pub fn append_liquidity_sources(
|
||||||
deps: DepsMut,
|
deps: DepsMut,
|
||||||
liquidity_sources: Vec<HexBinary>,
|
new_liquidity_sources: Vec<LiquiditySource>,
|
||||||
) -> Result<Response, ContractError> {
|
) -> Result<(), ContractError> {
|
||||||
// validate liquidity sources as public keys
|
let epoch = current_epoch_key(LIQUIDITY_SOURCES_KEY, deps.storage)?;
|
||||||
liquidity_sources
|
let mut liquidity_sources = LIQUIDITY_SOURCES
|
||||||
.iter()
|
.may_load(deps.storage, &epoch)?
|
||||||
.try_for_each(|ls| VerifyingKey::from_sec1_bytes(ls).map(|_| ()))?;
|
.unwrap_or_default();
|
||||||
|
|
||||||
// store the liquidity sources
|
let mut new_sources = vec![];
|
||||||
LiquiditySourcesItem::new_dyn(current_epoch_key(LIQUIDITY_SOURCES_KEY, deps.storage)?)
|
for liquidity_source in new_liquidity_sources {
|
||||||
.update(deps.storage, |mut ls| {
|
// Validate the Cosmos address
|
||||||
ls.clear();
|
let address = deps
|
||||||
ls.extend(liquidity_sources);
|
.api
|
||||||
Ok::<_, ContractError>(ls)
|
.addr_validate(&liquidity_source.address.to_string())?;
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(Response::default())
|
let liquidity_source = LiquiditySource {
|
||||||
|
address: address.clone(),
|
||||||
|
source_type: liquidity_source.source_type,
|
||||||
|
};
|
||||||
|
|
||||||
|
new_sources.push(liquidity_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
liquidity_sources.append(&mut new_sources);
|
||||||
|
|
||||||
|
// Save the new liquidity sources
|
||||||
|
LIQUIDITY_SOURCES.save(deps.storage, &epoch, &liquidity_sources)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn submit_setoffs(
|
pub fn submit_setoffs(
|
||||||
mut deps: DepsMut,
|
deps: DepsMut,
|
||||||
env: Env,
|
_env: Env,
|
||||||
setoffs_enc: BTreeMap<RawHash, SettleOff>,
|
setoffs_enc: BTreeMap<RawHash, SettleOff>,
|
||||||
) -> Result<Response, ContractError> {
|
) -> Result<Response, ContractError> {
|
||||||
// store the `BTreeMap<RawHash, RawCipherText>`
|
// Store the setoffs
|
||||||
SetoffsItem::new_dyn(previous_epoch_key(SETOFFS_KEY, deps.storage)?)
|
SetoffsItem::new_dyn(previous_epoch_key(SETOFFS_KEY, deps.storage)?)
|
||||||
.save(deps.storage, &setoffs_enc)?;
|
.save(deps.storage, &setoffs_enc)?;
|
||||||
|
|
||||||
|
let mut messages = vec![];
|
||||||
|
|
||||||
for (_, so) in setoffs_enc {
|
for (_, so) in setoffs_enc {
|
||||||
if let SettleOff::Transfer(t) = so {
|
if let SettleOff::Transfer(t) = so {
|
||||||
let info = MessageInfo {
|
// Check if either payer or payee is a liquidity source
|
||||||
sender: env.contract.address.clone(),
|
let payer_source = find_liquidity_source(deps.storage, &t.payer)?;
|
||||||
funds: vec![],
|
let payee_source = find_liquidity_source(deps.storage, &t.payee)?;
|
||||||
};
|
|
||||||
|
|
||||||
execute_mint(
|
match (payer_source, payee_source) {
|
||||||
deps.branch(),
|
(Some(source), None) => {
|
||||||
env.clone(),
|
// Payer is a liquidity source
|
||||||
info.clone(),
|
let msg = create_transfer_message(&source, &t, true)?;
|
||||||
t.payee.to_string(),
|
messages.push(msg);
|
||||||
t.amount.into(),
|
}
|
||||||
)?;
|
(None, Some(source)) => {
|
||||||
|
// Payee is a liquidity source
|
||||||
let payer = deps.api.addr_validate(&t.payer.to_string())?;
|
let msg = create_transfer_message(&source, &t, false)?;
|
||||||
let info = MessageInfo {
|
messages.push(msg);
|
||||||
sender: payer,
|
}
|
||||||
funds: vec![],
|
(_, _) => {
|
||||||
};
|
// As of now, transfers should only be between a user and liquidity source.
|
||||||
|
return Err(ContractError::LiquiditySourceNotFound {});
|
||||||
execute_burn(deps.branch(), env.clone(), info, t.amount.into())?;
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Response::new().add_attribute("action", "submit_setoffs"))
|
Ok(Response::new()
|
||||||
|
.add_submessages(messages)
|
||||||
|
.add_attribute("action", "submit_setoffs"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_liquidity_source(
|
||||||
|
storage: &dyn Storage,
|
||||||
|
address: &Addr,
|
||||||
|
) -> Result<Option<LiquiditySource>, ContractError> {
|
||||||
|
// TODO: check that .ok() is correct here
|
||||||
|
let liquidity_sources = LIQUIDITY_SOURCES.load(
|
||||||
|
storage,
|
||||||
|
&previous_epoch_key(LIQUIDITY_SOURCES_KEY, storage)?,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(liquidity_sources
|
||||||
|
.into_iter()
|
||||||
|
.find(|lqs| lqs.address == address))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_transfer_message(
|
||||||
|
source: &LiquiditySource,
|
||||||
|
transfer: &Transfer,
|
||||||
|
is_payer: bool,
|
||||||
|
) -> Result<SubMsg, ContractError> {
|
||||||
|
let msg = match source.source_type {
|
||||||
|
LiquiditySourceType::Escrow => {
|
||||||
|
let (payer, payee, amount) = if is_payer {
|
||||||
|
(
|
||||||
|
transfer.payer.to_string(),
|
||||||
|
transfer.payee.to_string(),
|
||||||
|
vec![transfer.amount.clone()],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// If the liquidity source is the payee, we swap payer and payee
|
||||||
|
(
|
||||||
|
transfer.payee.to_string(),
|
||||||
|
transfer.payer.to_string(),
|
||||||
|
vec![transfer.amount.clone()],
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
WasmMsg::Execute {
|
||||||
|
contract_addr: source.address.to_string(),
|
||||||
|
msg: to_json_binary(&EscrowExecuteMsg::ExecuteSetoff {
|
||||||
|
payer,
|
||||||
|
payee,
|
||||||
|
amount,
|
||||||
|
})?,
|
||||||
|
funds: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LiquiditySourceType::Overdraft => {
|
||||||
|
if is_payer {
|
||||||
|
let increase_msg = WasmMsg::Execute {
|
||||||
|
contract_addr: source.address.to_string(),
|
||||||
|
msg: to_json_binary(&OverdraftExecuteMsg::IncreaseBalance {
|
||||||
|
receiver: transfer.payee.clone(),
|
||||||
|
amount: transfer.amount.1,
|
||||||
|
})?,
|
||||||
|
funds: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
increase_msg
|
||||||
|
} else {
|
||||||
|
let decrease_msg = WasmMsg::Execute {
|
||||||
|
contract_addr: source.address.to_string(),
|
||||||
|
msg: to_json_binary(&OverdraftExecuteMsg::DecreaseBalance {
|
||||||
|
receiver: transfer.payer.clone(),
|
||||||
|
amount: transfer.amount.1,
|
||||||
|
})?,
|
||||||
|
funds: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
decrease_msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LiquiditySourceType::External => {
|
||||||
|
return Err(ContractError::UnsupportedLiquiditySource {})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(SubMsg::new(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_clearing(deps: DepsMut) -> Result<Response, ContractError> {
|
pub fn init_clearing(deps: DepsMut) -> Result<Response, ContractError> {
|
||||||
EPOCH_COUNTER.update(deps.storage, |mut counter| -> StdResult<_> {
|
EPOCH_COUNTER.update(deps.storage, |counter| -> StdResult<_> {
|
||||||
counter = counter.saturating_add(Uint64::from(1u64));
|
counter.checked_add(Uint64::new(1))?;
|
||||||
Ok(counter)
|
Ok(counter)
|
||||||
})?;
|
})?;
|
||||||
Ok(Response::new().add_attribute("action", "init_clearing"))
|
Ok(Response::new().add_attribute("action", "init_clearing"))
|
||||||
|
@ -259,7 +349,7 @@ pub mod query {
|
||||||
use crate::{
|
use crate::{
|
||||||
msg::{GetAllSetoffsResponse, GetLiquiditySourcesResponse},
|
msg::{GetAllSetoffsResponse, GetLiquiditySourcesResponse},
|
||||||
state::{
|
state::{
|
||||||
current_epoch_key, epoch_key, previous_epoch_key, LiquiditySourcesItem, SetoffsItem,
|
current_epoch_key, epoch_key, previous_epoch_key, SetoffsItem, LIQUIDITY_SOURCES,
|
||||||
LIQUIDITY_SOURCES_KEY, SETOFFS_KEY,
|
LIQUIDITY_SOURCES_KEY, SETOFFS_KEY,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -272,6 +362,7 @@ pub mod query {
|
||||||
Ok(GetAllSetoffsResponse { setoffs })
|
Ok(GetAllSetoffsResponse { setoffs })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to get liquidity sources for a specific epoch
|
||||||
pub fn get_liquidity_sources(
|
pub fn get_liquidity_sources(
|
||||||
deps: Deps,
|
deps: Deps,
|
||||||
epoch: Option<Uint64>,
|
epoch: Option<Uint64>,
|
||||||
|
@ -281,10 +372,8 @@ pub mod query {
|
||||||
Some(e) => epoch_key(LIQUIDITY_SOURCES_KEY, e)?,
|
Some(e) => epoch_key(LIQUIDITY_SOURCES_KEY, e)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let liquidity_sources = LiquiditySourcesItem::new_dyn(epoch_key)
|
let liquidity_sources = LIQUIDITY_SOURCES.load(deps.storage, &epoch_key)?;
|
||||||
.load(deps.storage)?
|
|
||||||
.into_iter()
|
|
||||||
.collect();
|
|
||||||
Ok(GetLiquiditySourcesResponse { liquidity_sources })
|
Ok(GetLiquiditySourcesResponse { liquidity_sources })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use cosmwasm_std::StdError;
|
||||||
use cw20_base::ContractError as Cw20ContractError;
|
use cw20_base::ContractError as Cw20ContractError;
|
||||||
use hex::FromHexError;
|
use hex::FromHexError;
|
||||||
use k256::ecdsa::Error as K256Error;
|
use k256::ecdsa::Error as K256Error;
|
||||||
use quartz_cw::error::Error as QuartzError;
|
use quartz_common::contract::error::Error as QuartzError;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -16,9 +16,15 @@ pub enum ContractError {
|
||||||
#[error("Unauthorized")]
|
#[error("Unauthorized")]
|
||||||
Unauthorized,
|
Unauthorized,
|
||||||
|
|
||||||
|
#[error("Liquidity source not found")]
|
||||||
|
LiquiditySourceNotFound,
|
||||||
|
|
||||||
#[error("Duplicate entry found")]
|
#[error("Duplicate entry found")]
|
||||||
DuplicateEntry,
|
DuplicateEntry,
|
||||||
|
|
||||||
|
#[error("No entry found")]
|
||||||
|
NoLiquiditySourcesFound,
|
||||||
|
|
||||||
#[error("Not Secp256K1")]
|
#[error("Not Secp256K1")]
|
||||||
K256(K256Error),
|
K256(K256Error),
|
||||||
|
|
||||||
|
@ -30,6 +36,9 @@ pub enum ContractError {
|
||||||
|
|
||||||
#[error("Cw20 error: {0}")]
|
#[error("Cw20 error: {0}")]
|
||||||
Cw20(Cw20ContractError),
|
Cw20(Cw20ContractError),
|
||||||
|
|
||||||
|
#[error("Unsupported liquidity source")]
|
||||||
|
UnsupportedLiquiditySource,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<K256Error> for ContractError {
|
impl From<K256Error> for ContractError {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#![deny(
|
// #![deny(
|
||||||
warnings,
|
// warnings,
|
||||||
trivial_casts,
|
// trivial_casts,
|
||||||
trivial_numeric_casts,
|
// trivial_numeric_casts,
|
||||||
unused_import_braces,
|
// unused_import_braces,
|
||||||
unused_qualifications
|
// unused_qualifications
|
||||||
)]
|
// )]
|
||||||
#![forbid(unsafe_code)]
|
// #![forbid(unsafe_code)]
|
||||||
|
|
||||||
pub mod contract;
|
pub mod contract;
|
||||||
mod error;
|
mod error;
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use cosmwasm_schema::{cw_serde, QueryResponses};
|
use cosmwasm_schema::{cw_serde, QueryResponses};
|
||||||
use cosmwasm_std::{HexBinary, Uint64};
|
use cosmwasm_std::{Addr, HexBinary, Uint128, Uint64};
|
||||||
use quartz_cw::{
|
use quartz_common::contract::{
|
||||||
msg::execute::attested::{RawAttested, RawAttestedMsgSansHandler, RawDefaultAttestation},
|
msg::execute::attested::{RawAttested, RawAttestedMsgSansHandler, RawDefaultAttestation},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::state::{RawHash, SettleOff};
|
use crate::state::{LiquiditySource, RawHash, SettleOff};
|
||||||
|
|
||||||
type AttestedMsg<M, RA> = RawAttested<RawAttestedMsgSansHandler<M>, RA>;
|
pub type AttestedMsg<M, RA> = RawAttested<RawAttestedMsgSansHandler<M>, RA>;
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct InstantiateMsg<RA = RawDefaultAttestation>(pub QuartzInstantiateMsg<RA>);
|
pub struct InstantiateMsg<RA = RawDefaultAttestation>(pub QuartzInstantiateMsg<RA>);
|
||||||
|
@ -27,11 +27,44 @@ pub enum ExecuteMsg<RA = RawDefaultAttestation> {
|
||||||
SetLiquiditySources(execute::SetLiquiditySourcesMsg),
|
SetLiquiditySources(execute::SetLiquiditySourcesMsg),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Added this back here because adding overdraft contract as a dependency is causing errors. Overdraft isn't correctly disabling entrypoints when acting as a dependency
|
||||||
|
#[cw_serde]
|
||||||
|
pub enum OverdraftExecuteMsg {
|
||||||
|
DrawCredit {
|
||||||
|
receiver: Addr,
|
||||||
|
amount: Uint128,
|
||||||
|
},
|
||||||
|
DrawCreditFromTender {
|
||||||
|
debtor: Addr,
|
||||||
|
amount: Uint128,
|
||||||
|
},
|
||||||
|
TransferCreditFromTender {
|
||||||
|
sender: Addr,
|
||||||
|
receiver: Addr,
|
||||||
|
amount: Uint128,
|
||||||
|
},
|
||||||
|
IncreaseBalance {
|
||||||
|
receiver: Addr,
|
||||||
|
amount: Uint128,
|
||||||
|
},
|
||||||
|
DecreaseBalance {
|
||||||
|
receiver: Addr,
|
||||||
|
amount: Uint128,
|
||||||
|
},
|
||||||
|
Lock {},
|
||||||
|
Unlock {},
|
||||||
|
AddOwner {
|
||||||
|
new: Addr,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
pub mod execute {
|
pub mod execute {
|
||||||
use quartz_cw::{msg::execute::attested::HasUserData, state::UserData};
|
use cosmwasm_std::Uint128;
|
||||||
|
use quartz_common::contract::{msg::execute::attested::HasUserData, state::UserData};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::state::LiquiditySource;
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct FaucetMintMsg {
|
pub struct FaucetMintMsg {
|
||||||
|
@ -56,7 +89,7 @@ pub mod execute {
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct SubmitObligationsMsg {
|
pub struct SubmitObligationsMsg {
|
||||||
pub obligations: Vec<SubmitObligationMsg>,
|
pub obligations: Vec<SubmitObligationMsg>,
|
||||||
pub liquidity_sources: Vec<HexBinary>,
|
pub liquidity_sources: Vec<LiquiditySource>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
|
@ -86,7 +119,16 @@ pub mod execute {
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct SetLiquiditySourcesMsg {
|
pub struct SetLiquiditySourcesMsg {
|
||||||
pub liquidity_sources: Vec<HexBinary>,
|
pub liquidity_sources: Vec<LiquiditySource>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cw_serde]
|
||||||
|
pub enum EscrowExecuteMsg {
|
||||||
|
ExecuteSetoff {
|
||||||
|
payer: String,
|
||||||
|
payee: String,
|
||||||
|
amount: Vec<(String, Uint128)>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +151,7 @@ pub struct GetAllSetoffsResponse {
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct GetLiquiditySourcesResponse {
|
pub struct GetLiquiditySourcesResponse {
|
||||||
pub liquidity_sources: Vec<HexBinary>,
|
pub liquidity_sources: Vec<LiquiditySource>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::{cmp::Ordering, collections::BTreeMap};
|
||||||
|
|
||||||
use cosmwasm_schema::cw_serde;
|
use cosmwasm_schema::cw_serde;
|
||||||
use cosmwasm_std::{HexBinary, StdError, Storage, Uint64};
|
use cosmwasm_std::{Addr, HexBinary, StdError, Storage, Uint128, Uint64};
|
||||||
use cw_storage_plus::Item;
|
use cw_storage_plus::{Item, Map};
|
||||||
use quartz_cw::state::EPOCH_COUNTER;
|
use quartz_common::contract::state::EPOCH_COUNTER;
|
||||||
|
|
||||||
pub type RawHash = HexBinary;
|
pub type RawHash = HexBinary;
|
||||||
pub type RawCipherText = HexBinary;
|
pub type RawCipherText = HexBinary;
|
||||||
|
|
||||||
pub type ObligationsItem = Item<BTreeMap<RawHash, RawCipherText>>;
|
pub type ObligationsItem = Item<BTreeMap<RawHash, RawCipherText>>;
|
||||||
pub type SetoffsItem = Item<BTreeMap<RawHash, SettleOff>>;
|
pub type SetoffsItem = Item<BTreeMap<RawHash, SettleOff>>;
|
||||||
pub type LiquiditySourcesItem = Item<BTreeSet<HexBinary>>;
|
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
@ -19,9 +18,9 @@ pub struct State {
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct Transfer {
|
pub struct Transfer {
|
||||||
pub payer: String,
|
pub payer: Addr,
|
||||||
pub payee: String,
|
pub payee: Addr,
|
||||||
pub amount: u64,
|
pub amount: (String, Uint128),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
|
@ -31,14 +30,43 @@ pub enum SettleOff {
|
||||||
Transfer(Transfer),
|
Transfer(Transfer),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cw_serde]
|
||||||
|
#[derive(Copy)]
|
||||||
|
pub enum LiquiditySourceType {
|
||||||
|
Escrow,
|
||||||
|
Overdraft,
|
||||||
|
External,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cw_serde]
|
||||||
|
pub struct LiquiditySource {
|
||||||
|
pub address: Addr,
|
||||||
|
pub source_type: LiquiditySourceType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::cmp::Ord for LiquiditySource {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.address.cmp(&other.address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for LiquiditySource {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.address.cmp(&other.address))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PartialEq implemented in #[cw_serde]
|
||||||
|
impl Eq for LiquiditySource {}
|
||||||
|
|
||||||
pub const STATE: Item<State> = Item::new("state");
|
pub const STATE: Item<State> = Item::new("state");
|
||||||
pub const OBLIGATIONS_KEY: &str = "obligations";
|
pub const OBLIGATIONS_KEY: &str = "obligations";
|
||||||
pub const SETOFFS_KEY: &str = "setoffs";
|
pub const SETOFFS_KEY: &str = "setoffs";
|
||||||
pub const LIQUIDITY_SOURCES_KEY: &str = "liquidity_sources";
|
pub const LIQUIDITY_SOURCES_KEY: &str = "epoch_liquidity_sources";
|
||||||
|
pub const LIQUIDITY_SOURCES: Map<&str, Vec<LiquiditySource>> = Map::new("liquidity_sources");
|
||||||
|
|
||||||
pub fn current_epoch_key(key: &str, storage: &dyn Storage) -> Result<String, StdError> {
|
pub fn current_epoch_key(key: &str, storage: &dyn Storage) -> Result<String, StdError> {
|
||||||
let epoch = EPOCH_COUNTER.load(storage)?;
|
epoch_key(key, EPOCH_COUNTER.load(storage)?)
|
||||||
epoch_key(key, epoch.into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn previous_epoch_key(key: &str, storage: &dyn Storage) -> Result<String, StdError> {
|
pub fn previous_epoch_key(key: &str, storage: &dyn Storage) -> Result<String, StdError> {
|
||||||
|
|
|
@ -1,11 +1,22 @@
|
||||||
[package]
|
[package]
|
||||||
name = "quartz-app-mtcs-enclave"
|
name = "mtcs-enclave"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Informal Systems <hello@informal.systems>"]
|
authors = ["Informal Systems <hello@informal.systems>"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = 3
|
||||||
|
debug = false
|
||||||
|
rpath = false
|
||||||
|
lto = true
|
||||||
|
debug-assertions = false
|
||||||
|
codegen-units = 1
|
||||||
|
panic = 'abort'
|
||||||
|
incremental = false
|
||||||
|
overflow-checks = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
mock-sgx = ["quartz-cw/mock-sgx", "quartz-enclave/mock-sgx"]
|
mock-sgx = ["quartz-common/mock-sgx-cw", "quartz-common/mock-sgx-enclave"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# external
|
# external
|
||||||
|
@ -28,17 +39,14 @@ tendermint.workspace = true
|
||||||
tendermint-light-client.workspace = true
|
tendermint-light-client.workspace = true
|
||||||
|
|
||||||
# quartz
|
# quartz
|
||||||
cw-proof.workspace = true
|
|
||||||
cw-tee-mtcs.workspace = true
|
cw-tee-mtcs.workspace = true
|
||||||
cycles-sync.workspace = true
|
|
||||||
mtcs.workspace = true
|
mtcs.workspace = true
|
||||||
quartz-cw.workspace = true
|
|
||||||
quartz-enclave.workspace = true
|
# quartz
|
||||||
quartz-proto.workspace = true
|
quartz-common = { workspace = true, features = ["full"]}
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
cw-multi-test = "2.0.0"
|
cw-multi-test = "2.0.0"
|
||||||
serde_json = "1.0.113"
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build.workspace = true
|
tonic-build.workspace = true
|
||||||
|
|
1
apps/mtcs/enclave/src/lib.rs
Normal file
1
apps/mtcs/enclave/src/lib.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod proto;
|
|
@ -15,6 +15,7 @@
|
||||||
mod cli;
|
mod cli;
|
||||||
mod mtcs_server;
|
mod mtcs_server;
|
||||||
mod proto;
|
mod proto;
|
||||||
|
mod types;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
|
@ -25,12 +26,14 @@ use clap::Parser;
|
||||||
use cli::Cli;
|
use cli::Cli;
|
||||||
use mtcs_server::MtcsService;
|
use mtcs_server::MtcsService;
|
||||||
use proto::clearing_server::ClearingServer as MtcsServer;
|
use proto::clearing_server::ClearingServer as MtcsServer;
|
||||||
use quartz_cw::state::{Config, LightClientOpts};
|
use quartz_common::{
|
||||||
use quartz_enclave::{
|
contract::state::{Config, LightClientOpts},
|
||||||
attestor::{Attestor, DefaultAttestor},
|
enclave::{
|
||||||
server::CoreService,
|
attestor::{Attestor, DefaultAttestor},
|
||||||
|
server::CoreService,
|
||||||
|
},
|
||||||
|
proto::core_server::CoreServer,
|
||||||
};
|
};
|
||||||
use quartz_proto::quartz::core_server::CoreServer;
|
|
||||||
use tonic::transport::Server;
|
use tonic::transport::Server;
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
|
|
|
@ -1,34 +1,37 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::{BTreeMap, BTreeSet},
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use cosmrs::{tendermint::account::Id as TmAccountId, AccountId};
|
use cosmwasm_std::{Addr, HexBinary, Uint128};
|
||||||
use cosmwasm_std::HexBinary;
|
|
||||||
//TODO: get rid of this
|
//TODO: get rid of this
|
||||||
use cw_tee_mtcs::{
|
use cw_tee_mtcs::{
|
||||||
msg::execute::SubmitSetoffsMsg,
|
msg::execute::SubmitSetoffsMsg,
|
||||||
state::{RawHash, SettleOff, Transfer},
|
state::{LiquiditySource, LiquiditySourceType, RawHash, SettleOff, Transfer},
|
||||||
};
|
};
|
||||||
use cycles_sync::types::RawObligation;
|
use ecies::decrypt;
|
||||||
use ecies::{decrypt, encrypt};
|
use k256::ecdsa::SigningKey;
|
||||||
use k256::ecdsa::{SigningKey, VerifyingKey};
|
|
||||||
use mtcs::{
|
use mtcs::{
|
||||||
algo::mcmf::primal_dual::PrimalDual, impls::complex_id::ComplexIdMtcs,
|
algo::mcmf::primal_dual::PrimalDual, impls::complex_id::ComplexIdMtcs,
|
||||||
obligation::SimpleObligation, prelude::DefaultMtcs, setoff::SimpleSetoff, Mtcs,
|
obligation::SimpleObligation, prelude::DefaultMtcs, setoff::SimpleSetoff, Mtcs,
|
||||||
};
|
};
|
||||||
use quartz_cw::{msg::execute::attested::RawAttested, state::Config};
|
use quartz_common::{
|
||||||
use quartz_enclave::{attestor::Attestor, server::ProofOfPublication};
|
contract::{msg::execute::attested::RawAttested, state::Config},
|
||||||
|
enclave::attestor::Attestor,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tonic::{Request, Response, Result as TonicResult, Status};
|
use tonic::{Request, Response, Result as TonicResult, Status};
|
||||||
|
|
||||||
use crate::proto::{clearing_server::Clearing, RunClearingRequest, RunClearingResponse};
|
use crate::{
|
||||||
|
proto::{clearing_server::Clearing, RunClearingRequest, RunClearingResponse},
|
||||||
|
types::ContractObligation,
|
||||||
|
};
|
||||||
|
|
||||||
pub type RawCipherText = HexBinary;
|
pub type RawCipherText = HexBinary;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MtcsService<A> {
|
pub struct MtcsService<A> {
|
||||||
config: Config,
|
config: Config, // TODO: this config is not used anywhere
|
||||||
sk: Arc<Mutex<Option<SigningKey>>>,
|
sk: Arc<Mutex<Option<SigningKey>>>,
|
||||||
attestor: A,
|
attestor: A,
|
||||||
}
|
}
|
||||||
|
@ -36,7 +39,7 @@ pub struct MtcsService<A> {
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct RunClearingMessage {
|
pub struct RunClearingMessage {
|
||||||
intents: BTreeMap<RawHash, RawCipherText>,
|
intents: BTreeMap<RawHash, RawCipherText>,
|
||||||
liquidity_sources: Vec<HexBinary>,
|
liquidity_sources: BTreeSet<LiquiditySource>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A> MtcsService<A>
|
impl<A> MtcsService<A>
|
||||||
|
@ -61,40 +64,25 @@ where
|
||||||
&self,
|
&self,
|
||||||
request: Request<RunClearingRequest>,
|
request: Request<RunClearingRequest>,
|
||||||
) -> TonicResult<Response<RunClearingResponse>> {
|
) -> TonicResult<Response<RunClearingResponse>> {
|
||||||
let message: ProofOfPublication<RunClearingMessage> = {
|
let message: RunClearingMessage = {
|
||||||
let message = request.into_inner().message;
|
let message = request.into_inner().message;
|
||||||
serde_json::from_str(&message).map_err(|e| Status::invalid_argument(e.to_string()))?
|
serde_json::from_str(&message).map_err(|e| Status::invalid_argument(e.to_string()))?
|
||||||
};
|
};
|
||||||
|
// TODO: ensure no duplicates somewhere else!
|
||||||
let (proof_value, message) = message
|
let liquidity_sources: Vec<LiquiditySource> =
|
||||||
.verify(self.config.light_client_opts())
|
message.liquidity_sources.into_iter().collect();
|
||||||
.map_err(Status::failed_precondition)?;
|
let digests_ciphertexts: BTreeMap<HexBinary, HexBinary> = message.intents;
|
||||||
|
|
||||||
let proof_value_matches_msg =
|
|
||||||
serde_json::to_string(&message.intents).is_ok_and(|s| s.as_bytes() == proof_value);
|
|
||||||
if !proof_value_matches_msg {
|
|
||||||
return Err(Status::failed_precondition("proof verification"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let digests_ciphertexts = message.intents;
|
|
||||||
let (digests, ciphertexts): (Vec<_>, Vec<_>) = digests_ciphertexts.into_iter().unzip();
|
let (digests, ciphertexts): (Vec<_>, Vec<_>) = digests_ciphertexts.into_iter().unzip();
|
||||||
|
|
||||||
let sk = self.sk.lock().unwrap();
|
let sk = self.sk.lock().unwrap();
|
||||||
let obligations: Vec<SimpleObligation<_, i64>> = ciphertexts
|
let obligations: Vec<SimpleObligation<LiquiditySource, i64>> = ciphertexts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|ciphertext| decrypt_obligation(sk.as_ref().unwrap(), &ciphertext))
|
.map(|ciphertext| decrypt_obligation(sk.as_ref().unwrap(), &ciphertext))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut mtcs = ComplexIdMtcs::wrapping(DefaultMtcs::new(PrimalDual::default()));
|
let mut mtcs = ComplexIdMtcs::wrapping(DefaultMtcs::new(PrimalDual::default()));
|
||||||
let setoffs: Vec<SimpleSetoff<_, i64>> = mtcs.run(obligations).unwrap();
|
|
||||||
|
|
||||||
let liquidity_sources: Vec<_> = message
|
|
||||||
.liquidity_sources
|
|
||||||
.into_iter()
|
|
||||||
.map(|ls| VerifyingKey::from_sec1_bytes(&ls))
|
|
||||||
.collect::<Result<_, _>>()
|
|
||||||
.map_err(|e| Status::invalid_argument(e.to_string()))?;
|
|
||||||
|
|
||||||
|
let setoffs: Vec<SimpleSetoff<LiquiditySource, i64>> = mtcs.run(obligations).unwrap();
|
||||||
let setoffs_enc: BTreeMap<RawHash, SettleOff> = setoffs
|
let setoffs_enc: BTreeMap<RawHash, SettleOff> = setoffs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|so| into_settle_offs(so, &liquidity_sources))
|
.map(|so| into_settle_offs(so, &liquidity_sources))
|
||||||
|
@ -103,7 +91,7 @@ where
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let msg = SubmitSetoffsMsg { setoffs_enc };
|
let msg = SubmitSetoffsMsg { setoffs_enc };
|
||||||
|
println!("setoff_msg: {:?}", msg);
|
||||||
let attestation = self
|
let attestation = self
|
||||||
.attestor
|
.attestor
|
||||||
.quote(msg.clone())
|
.quote(msg.clone())
|
||||||
|
@ -111,65 +99,93 @@ where
|
||||||
|
|
||||||
let attested_msg = RawAttested { msg, attestation };
|
let attested_msg = RawAttested { msg, attestation };
|
||||||
let message = serde_json::to_string(&attested_msg).unwrap();
|
let message = serde_json::to_string(&attested_msg).unwrap();
|
||||||
|
|
||||||
Ok(Response::new(RunClearingResponse { message }))
|
Ok(Response::new(RunClearingResponse { message }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Switch from Vec<_> to Vec<LiquiditySource>
|
||||||
fn into_settle_offs(
|
fn into_settle_offs(
|
||||||
so: SimpleSetoff<HexBinary, i64>,
|
so: SimpleSetoff<LiquiditySource, i64>,
|
||||||
liquidity_sources: &[VerifyingKey],
|
liquidity_sources: &Vec<LiquiditySource>,
|
||||||
) -> SettleOff {
|
) -> SettleOff {
|
||||||
let debtor_pk = VerifyingKey::from_sec1_bytes(&so.debtor).unwrap();
|
println!("\nsetoff: {:?}", so);
|
||||||
let creditor_pk = VerifyingKey::from_sec1_bytes(&so.creditor).unwrap();
|
println!("\nliq sources: {:?}", liquidity_sources);
|
||||||
|
|
||||||
if let Some(ls_pk) = liquidity_sources.iter().find(|ls| ls == &&debtor_pk) {
|
// TODO: temporary patch, fix issue with liquidity sources becoming type External
|
||||||
|
if liquidity_sources
|
||||||
|
.iter()
|
||||||
|
.map(|lqs| lqs.address.clone())
|
||||||
|
.collect::<Vec<Addr>>()
|
||||||
|
.contains(&so.debtor.address)
|
||||||
|
{
|
||||||
// A setoff on a tender should result in the creditor's (i.e. the tender receiver) balance
|
// A setoff on a tender should result in the creditor's (i.e. the tender receiver) balance
|
||||||
// decreasing by the setoff amount
|
// decreasing by the setoff amount
|
||||||
SettleOff::Transfer(Transfer {
|
SettleOff::Transfer(Transfer {
|
||||||
payer: wasm_address(creditor_pk),
|
payer: so.creditor.address.clone(),
|
||||||
payee: wasm_address(*ls_pk),
|
payee: so.debtor.address.clone(),
|
||||||
amount: so.set_off as u64,
|
// TODO: Include denominations
|
||||||
|
amount: ("peppicoin".to_owned(), Uint128::from(so.set_off as u128)),
|
||||||
})
|
})
|
||||||
} else if let Some(ls_pk) = liquidity_sources.iter().find(|ls| ls == &&creditor_pk) {
|
} else if liquidity_sources
|
||||||
|
.iter()
|
||||||
|
.map(|lqs| lqs.address.clone())
|
||||||
|
.collect::<Vec<Addr>>()
|
||||||
|
.contains(&so.creditor.address)
|
||||||
|
{
|
||||||
// A setoff on an acceptance should result in the debtor's (i.e. the acceptance initiator)
|
// A setoff on an acceptance should result in the debtor's (i.e. the acceptance initiator)
|
||||||
// balance increasing by the setoff amount
|
// balance increasing by the setoff amount
|
||||||
SettleOff::Transfer(Transfer {
|
SettleOff::Transfer(Transfer {
|
||||||
payer: wasm_address(*ls_pk),
|
payer: so.creditor.address.clone(),
|
||||||
payee: wasm_address(debtor_pk),
|
payee: so.debtor.address.clone(),
|
||||||
amount: so.set_off as u64,
|
amount: ("peppicoin".to_owned(), Uint128::from(so.set_off as u128)),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
SettleOff::SetOff(encrypt_setoff(so, debtor_pk, creditor_pk))
|
// TODO: Tracked by issue #22
|
||||||
|
|
||||||
|
// A no-op for the time being.
|
||||||
|
SettleOff::SetOff(vec![])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wasm_address(pk: VerifyingKey) -> String {
|
// fn wasm_address(pk: VerifyingKey) -> String {
|
||||||
let tm_pk = TmAccountId::from(pk);
|
// let tm_pk = TmAccountId::from(pk);
|
||||||
AccountId::new("wasm", tm_pk.as_bytes())
|
// AccountId::new("wasm", tm_pk.as_bytes())
|
||||||
.unwrap()
|
// .unwrap()
|
||||||
.to_string()
|
// .to_string()
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn encrypt_setoff(
|
// fn encrypt_setoff(
|
||||||
so: SimpleSetoff<HexBinary, i64>,
|
// so: SimpleSetoff<HexBinary, i64>,
|
||||||
debtor_pk: VerifyingKey,
|
// debtor_pk: VerifyingKey,
|
||||||
creditor_pk: VerifyingKey,
|
// creditor_pk: VerifyingKey,
|
||||||
) -> Vec<RawCipherText> {
|
// ) -> Vec<RawCipherText> {
|
||||||
let so_ser = serde_json::to_string(&so).expect("infallible serializer");
|
// 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_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();
|
// let so_creditor = encrypt(&creditor_pk.to_sec1_bytes(), so_ser.as_bytes()).unwrap();
|
||||||
|
|
||||||
vec![so_debtor.into(), so_creditor.into()]
|
// vec![so_debtor.into(), so_creditor.into()]
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn decrypt_obligation(
|
fn decrypt_obligation(
|
||||||
sk: &SigningKey,
|
sk: &SigningKey,
|
||||||
ciphertext: &RawCipherText,
|
ciphertext: &RawCipherText,
|
||||||
) -> SimpleObligation<HexBinary, i64> {
|
) -> SimpleObligation<LiquiditySource, i64> {
|
||||||
let o: RawObligation = {
|
let o: ContractObligation = {
|
||||||
let o = decrypt(&sk.to_bytes(), ciphertext).unwrap();
|
let o = decrypt(&sk.to_bytes(), ciphertext).unwrap();
|
||||||
serde_json::from_slice(&o).unwrap()
|
serde_json::from_slice(&o).unwrap()
|
||||||
};
|
};
|
||||||
SimpleObligation::new(None, o.debtor, o.creditor, i64::try_from(o.amount).unwrap()).unwrap()
|
|
||||||
|
SimpleObligation::new(
|
||||||
|
None,
|
||||||
|
LiquiditySource {
|
||||||
|
address: o.debtor,
|
||||||
|
source_type: LiquiditySourceType::External,
|
||||||
|
},
|
||||||
|
LiquiditySource {
|
||||||
|
address: o.creditor,
|
||||||
|
source_type: LiquiditySourceType::External,
|
||||||
|
},
|
||||||
|
i64::try_from(o.amount).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
59
apps/mtcs/enclave/src/types.rs
Normal file
59
apps/mtcs/enclave/src/types.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
use cosmwasm_std::{Addr, HexBinary};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ContractObligation {
|
||||||
|
pub debtor: Addr,
|
||||||
|
pub creditor: Addr,
|
||||||
|
pub amount: u64,
|
||||||
|
#[serde(default)]
|
||||||
|
pub salt: HexBinary,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct RawObligation {
|
||||||
|
pub debtor: HexBinary,
|
||||||
|
pub creditor: HexBinary,
|
||||||
|
pub amount: u64,
|
||||||
|
#[serde(default)]
|
||||||
|
pub salt: HexBinary,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct RawEncryptedObligation {
|
||||||
|
pub digest: HexBinary,
|
||||||
|
pub ciphertext: HexBinary,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct SubmitObligationsMsg {
|
||||||
|
pub submit_obligations: SubmitObligationsMsgInner,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct SubmitObligationsMsgInner {
|
||||||
|
pub obligations: Vec<RawEncryptedObligation>,
|
||||||
|
pub liquidity_sources: Vec<Addr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum RawSetOff {
|
||||||
|
SetOff(Vec<HexBinary>),
|
||||||
|
Transfer(RawSetOffTransfer),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct RawSetOffTransfer {
|
||||||
|
pub payer: String,
|
||||||
|
pub payee: String,
|
||||||
|
pub amount: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct RawOffset {
|
||||||
|
pub debtor: HexBinary,
|
||||||
|
pub creditor: HexBinary,
|
||||||
|
pub amount: u64,
|
||||||
|
pub set_off: u64,
|
||||||
|
}
|
4723
apps/mtcs/scripts/Cargo.lock
generated
Normal file
4723
apps/mtcs/scripts/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
58
apps/mtcs/scripts/Cargo.toml
Normal file
58
apps/mtcs/scripts/Cargo.toml
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
[package]
|
||||||
|
name = "scripts"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
[[bin]]
|
||||||
|
name = "listen"
|
||||||
|
path = "src/bin/listen.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "handshake"
|
||||||
|
path = "src/bin/handshake.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "deploy"
|
||||||
|
path = "src/bin/deploy.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap.workspace = true
|
||||||
|
color-eyre.workspace = true
|
||||||
|
ecies.workspace = true
|
||||||
|
hex.workspace = true
|
||||||
|
k256.workspace = true
|
||||||
|
prost.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
|
tokio.workspace = true
|
||||||
|
tonic.workspace = true
|
||||||
|
once_cell = "1.19.0"
|
||||||
|
reqwest = { version = "0.12.2", default-features = false, features = ["json", "rustls-tls"] }
|
||||||
|
anyhow = "1.0.86"
|
||||||
|
base64 = "0.22.1"
|
||||||
|
subtle-encoding = "0.5.1"
|
||||||
|
tokio-tungstenite = "0.23.1"
|
||||||
|
futures-util = "0.3.30"
|
||||||
|
tendermint-rpc = { version ="0.36.0", features=["websocket-client", "http-client"]}
|
||||||
|
|
||||||
|
# cosmos
|
||||||
|
cosmrs.workspace = true
|
||||||
|
cosmwasm-std.workspace = true
|
||||||
|
tendermint.workspace = true
|
||||||
|
tendermint-light-client.workspace = true
|
||||||
|
|
||||||
|
# quartz
|
||||||
|
cw-proof.workspace = true
|
||||||
|
cw-tee-mtcs.workspace = true
|
||||||
|
mtcs.workspace = true
|
||||||
|
|
||||||
|
# todo remove unnecessary imports
|
||||||
|
|
||||||
|
cycles-sync = { workspace = true}
|
||||||
|
tm-prover = { workspace = true}
|
||||||
|
quartz-common = { workspace = true, features=["contract"]}
|
||||||
|
quartz-tee-ra = { workspace = true}
|
||||||
|
mtcs-enclave = { path = "../enclave", optional = false}
|
||||||
|
regex = "1.10.5"
|
40
apps/mtcs/scripts/aggregator.sh
Normal file
40
apps/mtcs/scripts/aggregator.sh
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
ROOT=${ROOT:-$HOME}
|
||||||
|
DIR_MTCS="$ROOT/cycles-protocol/quartz-app"
|
||||||
|
DIR_CYCLES_SYNC="$ROOT/cycles-protocol/packages/cycles-sync/"
|
||||||
|
DIR_PROTO="$DIR_MTCS/enclave/proto"
|
||||||
|
|
||||||
|
OVERDRAFT=wasm199rcvzawgyse89k4smqdn4wp83f3q8rurg9vautppxh5cypydafqk9nt6q
|
||||||
|
|
||||||
|
cd $DIR_MTCS
|
||||||
|
|
||||||
|
export NODE_URL=143.244.186.205:26657
|
||||||
|
bash scripts/build.sh
|
||||||
|
|
||||||
|
cd $DIR_MTCS/scripts/scripts
|
||||||
|
CONTRACT=$(RUST_BACKTRACE=1 cargo run --bin deploy | tail -n 1)
|
||||||
|
|
||||||
|
|
||||||
|
echo "------------ built and deployed ------------"
|
||||||
|
|
||||||
|
PUB_KEY=$(RUST_BACKTRACE=1 cargo run --bin handshake -- --contract $CONTRACT | tail -n 1)
|
||||||
|
|
||||||
|
echo "PUB KEY: '$PUB_KEY'"
|
||||||
|
echo "------------ shook some hands ------------"
|
||||||
|
|
||||||
|
cd $DIR_CYCLES_SYNC
|
||||||
|
cargo run --bin submit -- --epoch-pk $PUB_KEY --mtcs $CONTRACT --overdraft $OVERDRAFT
|
||||||
|
echo "cargo run --bin submit -- --epoch-pk $PUB_KEY --mtcs $CONTRACT --overdraft $OVERDRAFT"
|
||||||
|
|
||||||
|
echo "------------ submitted obligations ------------"
|
||||||
|
|
||||||
|
# add contract to owners list in overdrafts contract
|
||||||
|
CURRENT_SEQUENCE=$(wasmd query account wasm14qdftsfk6fwn40l0xmruga08xlczl4g05npy70 --node http://$NODE_URL --output json | jq -r .sequence)
|
||||||
|
WASMD_OUTPUT=$(wasmd tx wasm execute $OVERDRAFT '{"add_owner": {"new": "'$CONTRACT'"}}' --from wasm14qdftsfk6fwn40l0xmruga08xlczl4g05npy70 --node http://$NODE_URL --chain-id testing --yes --sequence $CURRENT_SEQUENCE)
|
||||||
|
|
||||||
|
echo $WASMD_OUTPUT
|
||||||
|
echo "------------ added contract as owner of overdrafts ------------"
|
||||||
|
|
||||||
|
cd $DIR_MTCS/scripts/scripts
|
||||||
|
cargo run --bin listen -- --contract $CONTRACT
|
18
apps/mtcs/scripts/build.sh
Normal file
18
apps/mtcs/scripts/build.sh
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
ROOT=${ROOT:-$HOME}
|
||||||
|
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
echo "building enclave binary"
|
||||||
|
|
||||||
|
cd $ROOT/cycles-protocol/quartz-app/enclave/
|
||||||
|
CARGO_TARGET_DIR=./target cargo build --release
|
||||||
|
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
echo "building cosmwasm contract binary"
|
||||||
|
|
||||||
|
|
||||||
|
cd $ROOT/cycles-protocol/quartz-app/contracts/cw-tee-mtcs/
|
||||||
|
bash build.sh
|
23
apps/mtcs/scripts/deploy.sh
Executable file
23
apps/mtcs/scripts/deploy.sh
Executable file
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
ROOT=${ROOT:-$HOME}
|
||||||
|
|
||||||
|
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
echo "instantiate"
|
||||||
|
cd $ROOT/cycles-protocol/packages/relayer/
|
||||||
|
export INSTANTIATE_MSG=$(./scripts/relay.sh Instantiate | jq '{quartz: .}' )
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
|
||||||
|
echo "deploy contract"
|
||||||
|
cd $ROOT/cycles-protocol/quartz-app/contracts/cw-tee-mtcs
|
||||||
|
|
||||||
|
bash deploy-contract.sh target/wasm32-unknown-unknown/release/cw_tee_mtcs.wasm |& tee output
|
||||||
|
export CONTRACT=$(cat output | grep Address | awk '{print $NF}' | sed 's/\x1b\[[0-9;]*m//g')
|
||||||
|
echo $CONTRACT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
23
apps/mtcs/scripts/deploy45.sh
Executable file
23
apps/mtcs/scripts/deploy45.sh
Executable file
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
ROOT=${ROOT:-$HOME}
|
||||||
|
|
||||||
|
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
echo "instantiate"
|
||||||
|
cd $ROOT/cycles-protocol/packages/relayer/
|
||||||
|
export INSTANTIATE_MSG=$(./scripts/relay.sh Instantiate | jq '{quartz: .}' )
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
|
||||||
|
echo "deploy contract"
|
||||||
|
cd $ROOT/cycles-protocol/quartz-app/contracts/cw-tee-mtcs
|
||||||
|
|
||||||
|
bash deploy-contract.sh target/wasm32-unknown-unknown/release/cw_tee_mtcs.wasm |& tee output
|
||||||
|
export CONTRACT=$(cat output | grep Address | awk '{print $NF}' | sed 's/\x1b\[[0-9;]*m//g')
|
||||||
|
echo $CONTRACT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
117
apps/mtcs/scripts/handshake.sh
Executable file
117
apps/mtcs/scripts/handshake.sh
Executable file
|
@ -0,0 +1,117 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Perform the SessionCreate and SessionSetPubKey handshake between the contract and the sgx node
|
||||||
|
# Expects:
|
||||||
|
# - enclave is already initialized
|
||||||
|
# - contract is already deployed
|
||||||
|
# - apps/transfers/trusted.hash exists
|
||||||
|
#
|
||||||
|
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
ROOT=${ROOT:-$HOME}
|
||||||
|
|
||||||
|
NODE_URL=${NODE_URL:-127.0.0.1:26657}
|
||||||
|
|
||||||
|
if [ "$#" -eq 0 ]; then
|
||||||
|
echo "Usage: $0 <contract_address>"
|
||||||
|
exit 1 # Exit with a non-zero status to indicate an error
|
||||||
|
fi
|
||||||
|
|
||||||
|
CONTRACT="$1"
|
||||||
|
|
||||||
|
CMD="wasmd --node http://$NODE_URL"
|
||||||
|
|
||||||
|
cd "$ROOT/cycles-protocol/quartz-app/"
|
||||||
|
export TRUSTED_HASH=$(cat trusted.hash)
|
||||||
|
export TRUSTED_HEIGHT=$(cat trusted.height)
|
||||||
|
|
||||||
|
echo "using CMD: $CMD"
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
|
||||||
|
echo "create session"
|
||||||
|
|
||||||
|
# change to relay dir
|
||||||
|
cd $ROOT/cycles-protocol/packages/relayer
|
||||||
|
|
||||||
|
# execute SessionCreate on enclave
|
||||||
|
export EXECUTE_CREATE=$(./scripts/relay.sh SessionCreate)
|
||||||
|
|
||||||
|
# submit SessionCreate to contract
|
||||||
|
RES=$($CMD tx wasm execute "$CONTRACT" "$EXECUTE_CREATE" --from admin --chain-id testing -y --output json)
|
||||||
|
TX_HASH=$(echo $RES | jq -r '.["txhash"]')
|
||||||
|
|
||||||
|
# wait for tx to commit
|
||||||
|
while ! $CMD query tx $TX_HASH &> /dev/null; do
|
||||||
|
echo "... 🕐 waiting for tx"
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
# need to wait another block for light client proof
|
||||||
|
BLOCK_HEIGHT=$($CMD query block | jq .block.header.height)
|
||||||
|
|
||||||
|
echo "at heigh $BLOCK_HEIGHT. need to wait for a block"
|
||||||
|
while [[ $BLOCK_HEIGHT == $($CMD query block | jq .block.header.height) ]]; do
|
||||||
|
echo "... 🕐 waiting for another block"
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
# need to wait another block for light client proof
|
||||||
|
BLOCK_HEIGHT=$($CMD query block | jq .block.header.height)
|
||||||
|
echo "at heigh $BLOCK_HEIGHT. need to wait for a block"
|
||||||
|
while [[ $BLOCK_HEIGHT == $($CMD query block | jq .block.header.height) ]]; do
|
||||||
|
echo "... 🕐 waiting for another block"
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
|
||||||
|
echo "set session pk"
|
||||||
|
|
||||||
|
# change to prover dir
|
||||||
|
cd $ROOT/cycles-protocol/packages/tm-prover
|
||||||
|
export PROOF_FILE="light-client-proof.json"
|
||||||
|
if [ -f "$PROOF_FILE" ]; then
|
||||||
|
rm "$PROOF_FILE"
|
||||||
|
echo "removed old $PROOF_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# TODO: pass this in?
|
||||||
|
echo "trusted hash $TRUSTED_HASH"
|
||||||
|
echo "contract $CONTRACT"
|
||||||
|
|
||||||
|
# run prover to get light client proof
|
||||||
|
# TODO: assume this binary is pre-built?
|
||||||
|
# TODO: pass in addresses and chain id
|
||||||
|
cargo run -vvv -- --chain-id testing \
|
||||||
|
--primary "http://$NODE_URL" \
|
||||||
|
--witnesses "http://$NODE_URL" \
|
||||||
|
--trusted-height $TRUSTED_HEIGHT \
|
||||||
|
--trusted-hash $TRUSTED_HASH \
|
||||||
|
--contract-address $CONTRACT \
|
||||||
|
--storage-key "quartz_session" \
|
||||||
|
--trace-file $PROOF_FILE
|
||||||
|
|
||||||
|
export POP=$(cat $PROOF_FILE)
|
||||||
|
export POP_MSG=$(jq -nc --arg message "$POP" '$ARGS.named')
|
||||||
|
echo "hi"
|
||||||
|
# execute SessionSetPubKey on enclave
|
||||||
|
cd $ROOT/cycles-protocol/packages/relayer
|
||||||
|
export EXECUTE_SETPUB=$(./scripts/relay.sh SessionSetPubKey "$POP_MSG")
|
||||||
|
|
||||||
|
RES=$($CMD tx wasm execute "$CONTRACT" "$EXECUTE_SETPUB" --from admin --chain-id testing -y --output json)
|
||||||
|
TX_HASH=$(echo $RES | jq -r '.["txhash"]')
|
||||||
|
|
||||||
|
# wait for tx to commit
|
||||||
|
while ! $CMD query tx $TX_HASH &> /dev/null; do
|
||||||
|
echo "... 🕐 waiting for tx"
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
|
||||||
|
echo "check session success"
|
||||||
|
export NONCE_AND_KEY=$($CMD query wasm contract-state raw "$CONTRACT" $(printf '%s' "quartz_session" | hexdump -ve '/1 "%02X"') -o json | jq -r .data | base64 -d)
|
||||||
|
echo $NONCE_AND_KEY
|
||||||
|
export PUBKEY=$(echo $NONCE_AND_KEY | jq -r .pub_key)
|
||||||
|
|
101
apps/mtcs/scripts/listen.sh
Executable file
101
apps/mtcs/scripts/listen.sh
Executable file
|
@ -0,0 +1,101 @@
|
||||||
|
|
||||||
|
|
||||||
|
ROOT=${ROOT:-$HOME}
|
||||||
|
DIR_MTCS="$ROOT/cycles-protocol/quartz-app/"
|
||||||
|
DIR_PROTO="$DIR_MTCS/enclave/proto"
|
||||||
|
DEFAULT_NODE="127.0.0.1:26657"
|
||||||
|
NODE_URL="143.244.186.205:26657"
|
||||||
|
# Use the QUARTZ_PORT environment variable if set, otherwise default to 11090
|
||||||
|
QUARTZ_PORT="${QUARTZ_PORT:-11090}"
|
||||||
|
|
||||||
|
|
||||||
|
# Attestation constants
|
||||||
|
IAS_API_KEY="669244b3e6364b5888289a11d2a1726d"
|
||||||
|
RA_CLIENT_SPID="51CAF5A48B450D624AEFE3286D314894"
|
||||||
|
QUOTE_FILE="/tmp/${USER}_test.quote"
|
||||||
|
REPORT_FILE="/tmp/${USER}_datareport"
|
||||||
|
REPORT_SIG_FILE="/tmp/${USER}_datareportsig"
|
||||||
|
|
||||||
|
if [ "$#" -eq 0 ]; then
|
||||||
|
echo "Usage: $0 <contract_address>"
|
||||||
|
exit 1 # Exit with a non-zero status to indicate an error
|
||||||
|
fi
|
||||||
|
|
||||||
|
CONTRACT=$1
|
||||||
|
|
||||||
|
CMD="wasmd --node http://$NODE_URL"
|
||||||
|
|
||||||
|
WSURL="ws://$NODE_URL/websocket"
|
||||||
|
|
||||||
|
SUBSCRIBE="{\"jsonrpc\":\"2.0\",\"method\":\"subscribe\",\"params\":[\"tm.event='Tx' AND wasm._contract_address = '$CONTRACT' AND wasm.action='init_clearing'\"],\"id\":1}"
|
||||||
|
|
||||||
|
echo $SUBSCRIBE
|
||||||
|
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
echo "subscribe to events"
|
||||||
|
|
||||||
|
# cat keeps the stdin open so websocat doesnt close
|
||||||
|
(echo "$SUBSCRIBE"; cat) | websocat $WSURL | while read msg; do
|
||||||
|
if [[ "$msg" == '{"jsonrpc":"2.0","id":1,"result":{}}' ]]; then
|
||||||
|
echo "... subscribed"
|
||||||
|
echo "---------------------------------------------------------"
|
||||||
|
echo "... waiting for event"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$msg" | jq 'has("error")' > /dev/null; then
|
||||||
|
echo "... error msg $msg"
|
||||||
|
echo "---------------------------------------------------------"
|
||||||
|
echo "... waiting for event"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
echo "... received init_clearing event!"
|
||||||
|
echo $msg
|
||||||
|
|
||||||
|
echo "... fetching obligations"
|
||||||
|
|
||||||
|
export EPOCH=$($CMD query wasm contract-state raw "$CONTRACT" "65706f63685f636f756e746572" -o json | jq -r .data | base64 -d)
|
||||||
|
PREV_EPOCH=$((EPOCH - 1))
|
||||||
|
|
||||||
|
export OBLIGATIONS=$($CMD query wasm contract-state raw "$CONTRACT" $(printf '%s/%s' "$PREV_EPOCH" "obligations" | hexdump -ve '/1 "%02X"') -o json | jq -r .data | base64 -d)
|
||||||
|
export LIQUIDITY_SOURCES=$($CMD query wasm contract-state smart $CONTRACT '{"get_liquidity_sources": {"epoch": '$PREV_EPOCH'}}' -o json | jq -r .data.liquidity_sources)
|
||||||
|
|
||||||
|
COMBINED_JSON=$(jq -nc \
|
||||||
|
--argjson intents "$OBLIGATIONS" \
|
||||||
|
--argjson liquidity_sources "$LIQUIDITY_SOURCES" \
|
||||||
|
'{intents: $intents, liquidity_sources: $liquidity_sources}')
|
||||||
|
|
||||||
|
echo $COMBINED_JSON
|
||||||
|
|
||||||
|
# Wrap the combined JSON string into another JSON object with a "message" field
|
||||||
|
REQUEST_MSG=$(jq -nc --arg message "$COMBINED_JSON" '{"message": $message}')
|
||||||
|
|
||||||
|
echo "... executing mtcs"
|
||||||
|
export ATTESTED_MSG=$(grpcurl -plaintext -import-path "$DIR_PROTO" -proto mtcs.proto -d "$REQUEST_MSG" "127.0.0.1:$QUARTZ_PORT" mtcs.Clearing/Run | jq -c '.message | fromjson')
|
||||||
|
|
||||||
|
QUOTE=$(echo "$ATTESTED_MSG" | jq -c '.attestation')
|
||||||
|
MSG=$(echo "$ATTESTED_MSG" | jq -c '.msg')
|
||||||
|
|
||||||
|
# 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 "... submitting update"
|
||||||
|
|
||||||
|
|
||||||
|
export EXECUTE=$(jq -nc --argjson submit_setoffs "$(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')
|
||||||
|
$CMD tx wasm execute "$CONTRACT" "$EXECUTE" --from admin --chain-id testing -y --gas 2000000
|
||||||
|
|
||||||
|
|
||||||
|
echo " ... done"
|
||||||
|
echo "---------------------------------------------------------"
|
||||||
|
echo "... waiting for event"
|
||||||
|
done
|
||||||
|
|
||||||
|
|
80
apps/mtcs/scripts/src/bin/deploy.rs
Normal file
80
apps/mtcs/scripts/src/bin/deploy.rs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
use std::{env::current_dir, str::FromStr};
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use cosmrs::tendermint::chain::Id as ChainId;
|
||||||
|
use cw_tee_mtcs::msg::InstantiateMsg as MtcsInstantiateMsg;
|
||||||
|
use cycles_sync::wasmd_client::{CliWasmdClient, WasmdClient};
|
||||||
|
use quartz_common::contract::msg::RawInstantiateMsg;
|
||||||
|
use reqwest::Url;
|
||||||
|
use scripts::{
|
||||||
|
types::{Log, WasmdTxResponse},
|
||||||
|
utils::{block_tx_commit, run_relay},
|
||||||
|
};
|
||||||
|
use serde_json::json;
|
||||||
|
use tendermint::Hash;
|
||||||
|
use tendermint_rpc::HttpClient;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Parser)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct Cli {
|
||||||
|
#[clap(long, default_value = "143.244.186.205:26657")]
|
||||||
|
node_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), anyhow::Error> {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
let base_path = current_dir()?.join("../../../");
|
||||||
|
|
||||||
|
println!("\n🚀 Communicating with Relay to Instantiate...\n");
|
||||||
|
let init_msg: RawInstantiateMsg = run_relay(base_path.as_path(), "Instantiate", None)?; // need to define the return type
|
||||||
|
let init_msg: MtcsInstantiateMsg = MtcsInstantiateMsg(init_msg);
|
||||||
|
|
||||||
|
let httpurl = Url::parse(&format!("http://{}", cli.node_url))?;
|
||||||
|
let tmrpc_client = HttpClient::new(httpurl.as_str()).unwrap();
|
||||||
|
let wasmd_client = CliWasmdClient::new(Url::parse(httpurl.as_str())?);
|
||||||
|
|
||||||
|
println!("\n🚀 Deploying MTCS Contract\n");
|
||||||
|
let contract_path = base_path.join(
|
||||||
|
"quartz-app/contracts/cw-tee-mtcs/target/wasm32-unknown-unknown/release/cw_tee_mtcs.wasm",
|
||||||
|
);
|
||||||
|
// TODO: uncertain about the path -> string conversion
|
||||||
|
let deploy_output: WasmdTxResponse = serde_json::from_str(&wasmd_client.deploy(
|
||||||
|
&ChainId::from_str("testing")?,
|
||||||
|
String::from("wasm14qdftsfk6fwn40l0xmruga08xlczl4g05npy70"),
|
||||||
|
contract_path.as_path().to_string_lossy(),
|
||||||
|
)?)?;
|
||||||
|
|
||||||
|
let tx_hash =
|
||||||
|
Hash::from_str(&deploy_output.txhash).expect("Invalid hex string for transaction hash");
|
||||||
|
let res = block_tx_commit(&tmrpc_client, tx_hash).await?;
|
||||||
|
|
||||||
|
let log: Vec<Log> = serde_json::from_str(&res.tx_result.log)?;
|
||||||
|
let code_id: usize = log[0].events[1].attributes[1].value.parse()?;
|
||||||
|
|
||||||
|
println!("\n🚀 Instantiating MTCS Contract\n");
|
||||||
|
|
||||||
|
let deploy_output: WasmdTxResponse = serde_json::from_str(&wasmd_client.init(
|
||||||
|
&ChainId::from_str("testing")?,
|
||||||
|
String::from("wasm14qdftsfk6fwn40l0xmruga08xlczl4g05npy70"),
|
||||||
|
code_id,
|
||||||
|
json!(init_msg),
|
||||||
|
format!("MTCS Contract V{}", code_id),
|
||||||
|
)?)?;
|
||||||
|
|
||||||
|
let tx_hash =
|
||||||
|
Hash::from_str(&deploy_output.txhash).expect("Invalid hex string for transaction hash");
|
||||||
|
let res = block_tx_commit(&tmrpc_client, tx_hash).await?;
|
||||||
|
let log: Vec<Log> = serde_json::from_str(&res.tx_result.log)?;
|
||||||
|
let contract_addr: &String = &log[0].events[1].attributes[0].value;
|
||||||
|
|
||||||
|
println!("\n🚀 Successfully deployed and instantiated contract!");
|
||||||
|
println!("🆔 Code ID: {}", code_id);
|
||||||
|
println!("📌 Contract Address: {}", contract_addr);
|
||||||
|
|
||||||
|
println!("{contract_addr}");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
//RES=$($CMD tx wasm instantiate "$CODE_ID" "$INSTANTIATE_MSG" --from "$USER_ADDR" --label $LABEL $TXFLAG -y --no-admin --output json)
|
229
apps/mtcs/scripts/src/bin/handshake.rs
Normal file
229
apps/mtcs/scripts/src/bin/handshake.rs
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
use std::{env, env::current_dir, fs::File, io::Read, path::Path, str::FromStr};
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use clap::Parser;
|
||||||
|
use cosmrs::tendermint::chain::Id as ChainId; // TODO see if this redundancy in dependencies can be decreased
|
||||||
|
use cosmrs::AccountId;
|
||||||
|
use cw_tee_mtcs::msg::ExecuteMsg as MtcsExecuteMsg;
|
||||||
|
use cycles_sync::wasmd_client::{CliWasmdClient, WasmdClient};
|
||||||
|
use futures_util::stream::StreamExt;
|
||||||
|
use quartz_common::contract::prelude::QuartzExecuteMsg;
|
||||||
|
use reqwest::Url;
|
||||||
|
use scripts::{
|
||||||
|
types::WasmdTxResponse,
|
||||||
|
utils::{block_tx_commit, run_relay, wasmaddr_to_id},
|
||||||
|
};
|
||||||
|
use serde::Serialize;
|
||||||
|
use serde_json::json;
|
||||||
|
use tendermint::{block::Height, Hash};
|
||||||
|
use tendermint_rpc::{query::EventType, HttpClient, SubscriptionClient, WebSocketClient};
|
||||||
|
use tm_prover::{config::Config as TmProverConfig, prover::prove};
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Message<'a> {
|
||||||
|
message: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Parser)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct Cli {
|
||||||
|
/// Contract to listen to
|
||||||
|
#[arg(short, long, value_parser = wasmaddr_to_id)]
|
||||||
|
contract: AccountId,
|
||||||
|
/// Port enclave is listening on
|
||||||
|
#[arg(short, long, default_value = "11090")]
|
||||||
|
port: u16,
|
||||||
|
|
||||||
|
#[arg(
|
||||||
|
short,
|
||||||
|
long,
|
||||||
|
default_value = "wasm14qdftsfk6fwn40l0xmruga08xlczl4g05npy70"
|
||||||
|
)]
|
||||||
|
sender: String,
|
||||||
|
|
||||||
|
#[clap(long, default_value = "143.244.186.205:26657")]
|
||||||
|
node_url: String,
|
||||||
|
|
||||||
|
#[clap(long, default_value_t = default_rpc_addr())]
|
||||||
|
rpc_addr: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_rpc_addr() -> String {
|
||||||
|
env::var("RPC_URL").unwrap_or_else(|_| "http://127.0.0.1".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), anyhow::Error> {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
// Convert contract address string parameter to an AccountId
|
||||||
|
// TODO: is this all the address validation that's needed?
|
||||||
|
let httpurl = Url::parse(&format!("http://{}", cli.node_url))?;
|
||||||
|
let wsurl = format!("ws://{}/websocket", cli.node_url);
|
||||||
|
|
||||||
|
let tmrpc_client = HttpClient::new(httpurl.as_str()).unwrap();
|
||||||
|
|
||||||
|
let wasmd_client = CliWasmdClient::new(Url::parse(httpurl.as_str())?);
|
||||||
|
|
||||||
|
// test(&client, &wasmd_client).await?;
|
||||||
|
// panic!();
|
||||||
|
// read trusted hash and height from files
|
||||||
|
let base_path = current_dir()?.join("../../../");
|
||||||
|
let trusted_files_path = base_path.join("quartz-app/");
|
||||||
|
let (trusted_height, trusted_hash) = read_hash_height(trusted_files_path.as_path()).await?;
|
||||||
|
|
||||||
|
// run sessioncreate in relayer script
|
||||||
|
// export EXECUTE_CREATE=$(./scripts/relay.sh SessionCreate)
|
||||||
|
// TODO: this is not the right return type
|
||||||
|
let res: MtcsExecuteMsg = run_relay(base_path.as_path(), "SessionCreate", None)?; // need to define the return type
|
||||||
|
|
||||||
|
// submit SessionCreate to contract
|
||||||
|
|
||||||
|
// RES=$($CMD tx wasm execute "$CONTRACT" "$EXECUTE_CREATE" --from admin --chain-id testing -y --output json)
|
||||||
|
// TX_HASH=$(echo $RES | jq -r '.["txhash"]')
|
||||||
|
// make sure this is json
|
||||||
|
let output: WasmdTxResponse = serde_json::from_str(
|
||||||
|
wasmd_client
|
||||||
|
.tx_execute(
|
||||||
|
&cli.contract.clone(),
|
||||||
|
&ChainId::from_str("testing")?,
|
||||||
|
2000000,
|
||||||
|
cli.sender.clone(),
|
||||||
|
json!(res),
|
||||||
|
)?
|
||||||
|
.as_str(),
|
||||||
|
)?;
|
||||||
|
println!("\n\n SessionCreate tx output: {:?}", output);
|
||||||
|
|
||||||
|
// wait for tx to commit (in a loop?)
|
||||||
|
let tx_hash = Hash::from_str(&output.txhash).expect("Invalid hex string for transaction hash");
|
||||||
|
block_tx_commit(&tmrpc_client, tx_hash).await?;
|
||||||
|
|
||||||
|
// tendermint client subscription loop
|
||||||
|
// wait 2 blocks
|
||||||
|
two_block_waitoor(&wsurl).await?;
|
||||||
|
|
||||||
|
//cd $ROOT/cycles-protocol/packages/tm-prover
|
||||||
|
//export PROOF_FILE="light-client-proof.json"
|
||||||
|
// TODO: move all the proof related files into a directory in scripts dir
|
||||||
|
let proof_path = current_dir()?.join("../../../packages/tm-prover/light-client-proof.json");
|
||||||
|
println!("Proof path: {:?}", proof_path.to_str());
|
||||||
|
|
||||||
|
let config = TmProverConfig {
|
||||||
|
primary: httpurl.as_str().parse()?,
|
||||||
|
witnesses: httpurl.as_str().parse()?,
|
||||||
|
trusted_height,
|
||||||
|
trusted_hash,
|
||||||
|
trace_file: Some(proof_path.clone()),
|
||||||
|
verbose: "1".parse()?,
|
||||||
|
contract_address: cli.contract.clone(),
|
||||||
|
storage_key: "quartz_session".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(report) = prove(config).await {
|
||||||
|
return Err(anyhow!("Tendermint prover failed. Report: {}", report));
|
||||||
|
}
|
||||||
|
|
||||||
|
// read proof file
|
||||||
|
let proof = read_file(proof_path.as_path()).await?;
|
||||||
|
let json_msg = serde_json::to_string(&Message { message: &proof })?;
|
||||||
|
|
||||||
|
// execute SessionSetPubKey on enclave
|
||||||
|
// cd $ROOT/cycles-protocol/packages/relayer
|
||||||
|
// export EXECUTE_SETPUB=$(./scripts/relay.sh SessionSetPubKey "$POP_MSG")
|
||||||
|
|
||||||
|
let res: MtcsExecuteMsg = run_relay(
|
||||||
|
base_path.as_path(),
|
||||||
|
"SessionSetPubKey",
|
||||||
|
Some(json_msg.as_str()),
|
||||||
|
)?;
|
||||||
|
// submit SessionSetPubKey to contract
|
||||||
|
|
||||||
|
// RES=$($CMD tx wasm execute "$CONTRACT" "$EXECUTE_SETPUB" --from admin --chain-id testing -y --output json)
|
||||||
|
// TX_HASH=$(echo $RES | jq -r '.["txhash"]')
|
||||||
|
// wait for tx to commit
|
||||||
|
let output: WasmdTxResponse = serde_json::from_str(
|
||||||
|
wasmd_client
|
||||||
|
.tx_execute(
|
||||||
|
&cli.contract.clone(),
|
||||||
|
&ChainId::from_str("testing")?,
|
||||||
|
2000000,
|
||||||
|
cli.sender.clone(),
|
||||||
|
json!(res),
|
||||||
|
)?
|
||||||
|
.as_str(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
println!("\n\n SessionSetPubKey tx output: {:?}", output);
|
||||||
|
|
||||||
|
// wait for tx to commit (in a loop?)
|
||||||
|
let tx_hash = Hash::from_str(&output.txhash).expect("Invalid hex string for transaction hash");
|
||||||
|
|
||||||
|
block_tx_commit(&tmrpc_client, tx_hash).await?;
|
||||||
|
|
||||||
|
if let MtcsExecuteMsg::Quartz(QuartzExecuteMsg::RawSessionSetPubKey(quartz)) = res {
|
||||||
|
println!("\n\n\n{}", quartz.msg.pub_key()); // TODO: return this instead later
|
||||||
|
} else {
|
||||||
|
return Err(anyhow!("Invalid relay response from SessionSetPubKey"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// query results
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn two_block_waitoor(wsurl: &str) -> Result<(), anyhow::Error> {
|
||||||
|
let (client, driver) = WebSocketClient::new(wsurl).await.unwrap();
|
||||||
|
|
||||||
|
let driver_handle = tokio::spawn(async move { driver.run().await });
|
||||||
|
|
||||||
|
// Subscription functionality
|
||||||
|
let mut subs = client.subscribe(EventType::NewBlock.into()).await.unwrap();
|
||||||
|
|
||||||
|
// Wait 2 NewBlock events
|
||||||
|
let mut ev_count = 5_i32;
|
||||||
|
|
||||||
|
while let Some(res) = subs.next().await {
|
||||||
|
let ev = res.unwrap();
|
||||||
|
println!("Got event: {:?}", ev);
|
||||||
|
ev_count -= 1;
|
||||||
|
if ev_count < 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal to the driver to terminate.
|
||||||
|
client.close().unwrap();
|
||||||
|
// Await the driver's termination to ensure proper connection closure.
|
||||||
|
let _ = driver_handle.await.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_hash_height(base_path: &Path) -> Result<(Height, Hash), anyhow::Error> {
|
||||||
|
let height_path = base_path.join("trusted.height");
|
||||||
|
let trusted_height: Height = read_file(height_path.as_path()).await?.parse()?;
|
||||||
|
|
||||||
|
let hash_path = base_path.join("trusted.hash");
|
||||||
|
let trusted_hash: Hash = read_file(hash_path.as_path()).await?.parse()?;
|
||||||
|
|
||||||
|
Ok((trusted_height, trusted_hash))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_file(path: &Path) -> Result<String, anyhow::Error> {
|
||||||
|
// Open the file
|
||||||
|
let mut file = match File::open(path) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(anyhow!(format!("Error opening file {:?}: {:?}", path, e)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read the file contents into a string
|
||||||
|
let mut value = String::new();
|
||||||
|
if let Err(e) = file.read_to_string(&mut value) {
|
||||||
|
return Err(anyhow!(format!("Error reading file {:?}: {:?}", file, e)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(value.trim().to_owned())
|
||||||
|
}
|
250
apps/mtcs/scripts/src/bin/listen.rs
Normal file
250
apps/mtcs/scripts/src/bin/listen.rs
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
use std::{
|
||||||
|
collections::{BTreeMap, BTreeSet},
|
||||||
|
env,
|
||||||
|
process::Command,
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use base64::prelude::*;
|
||||||
|
use clap::Parser;
|
||||||
|
use cosmrs::{tendermint::chain::Id as ChainId, AccountId};
|
||||||
|
use cosmwasm_std::{Binary, HexBinary, Uint64};
|
||||||
|
use cw_tee_mtcs::{
|
||||||
|
msg::{
|
||||||
|
execute::SubmitSetoffsMsg, AttestedMsg, ExecuteMsg, GetLiquiditySourcesResponse,
|
||||||
|
QueryMsg::GetLiquiditySources,
|
||||||
|
},
|
||||||
|
state::LiquiditySource,
|
||||||
|
};
|
||||||
|
use cycles_sync::wasmd_client::{CliWasmdClient, QueryResult, WasmdClient};
|
||||||
|
use futures_util::stream::StreamExt;
|
||||||
|
use mtcs_enclave::proto::{clearing_client::ClearingClient, RunClearingRequest};
|
||||||
|
use quartz_common::contract::msg::execute::attested::{
|
||||||
|
EpidAttestation, RawAttested, RawAttestedMsgSansHandler,
|
||||||
|
};
|
||||||
|
use quartz_tee_ra::{intel_sgx::epid::types::ReportBody, IASReport};
|
||||||
|
use reqwest::Url;
|
||||||
|
use scripts::utils::wasmaddr_to_id;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::json;
|
||||||
|
use tendermint_rpc::{
|
||||||
|
query::{EventType, Query},
|
||||||
|
SubscriptionClient, WebSocketClient,
|
||||||
|
};
|
||||||
|
use tokio::{
|
||||||
|
fs::{self, File},
|
||||||
|
io::AsyncWriteExt,
|
||||||
|
};
|
||||||
|
use tonic::Request;
|
||||||
|
|
||||||
|
// TODO: import this from enclave or somewhere shared
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct RunClearingMessage {
|
||||||
|
intents: BTreeMap<HexBinary, HexBinary>,
|
||||||
|
liquidity_sources: BTreeSet<LiquiditySource>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Parser)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct Cli {
|
||||||
|
/// Contract to listen to
|
||||||
|
#[arg(short, long, value_parser = wasmaddr_to_id)]
|
||||||
|
contract: AccountId,
|
||||||
|
/// Port enclave is listening on
|
||||||
|
#[arg(short, long, default_value = "11090")]
|
||||||
|
port: u16,
|
||||||
|
|
||||||
|
#[arg(
|
||||||
|
short,
|
||||||
|
long,
|
||||||
|
default_value = "wasm14qdftsfk6fwn40l0xmruga08xlczl4g05npy70"
|
||||||
|
)]
|
||||||
|
sender: String,
|
||||||
|
|
||||||
|
#[clap(long, default_value = "143.244.186.205:26657")]
|
||||||
|
node_url: String,
|
||||||
|
|
||||||
|
#[clap(long, default_value_t = default_rpc_addr())]
|
||||||
|
rpc_addr: String,
|
||||||
|
|
||||||
|
#[arg(short, long, default_value = "dangush")]
|
||||||
|
user: String, // The filesys user for gramine filepaths. TODO: improve this
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_rpc_addr() -> String {
|
||||||
|
env::var("RPC_URL").unwrap_or_else(|_| "http://127.0.0.1".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), anyhow::Error> {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
// Subscribe to "init_clearing" events
|
||||||
|
let wsurl = format!("ws://{}/websocket", cli.node_url);
|
||||||
|
let (client, driver) = WebSocketClient::new(wsurl.as_str()).await.unwrap();
|
||||||
|
let driver_handle = tokio::spawn(async move { driver.run().await });
|
||||||
|
|
||||||
|
let mut subs = client
|
||||||
|
.subscribe(Query::from(EventType::Tx).and_contains("wasm.action", "init_clearing"))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
while subs.next().await.is_some() {
|
||||||
|
// On init_clearing, run process
|
||||||
|
if let Err(e) = handler(
|
||||||
|
&cli.contract,
|
||||||
|
cli.sender.clone(),
|
||||||
|
format!("{}:{}", cli.rpc_addr, cli.port),
|
||||||
|
&cli.node_url,
|
||||||
|
&cli.user,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
println!("{}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close connection
|
||||||
|
// Await the driver's termination to ensure proper connection closure.
|
||||||
|
client.close().unwrap();
|
||||||
|
let _ = driver_handle.await.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handler(
|
||||||
|
contract: &AccountId,
|
||||||
|
sender: String,
|
||||||
|
rpc_addr: String,
|
||||||
|
node_url: &str,
|
||||||
|
user: &str,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
let chain_id = &ChainId::from_str("testing")?;
|
||||||
|
let httpurl = Url::parse(&format!("http://{}", node_url))?;
|
||||||
|
let wasmd_client = CliWasmdClient::new(httpurl);
|
||||||
|
|
||||||
|
// Query obligations and liquidity sources from chain
|
||||||
|
let clearing_contents = query_chain(&wasmd_client, contract).await?;
|
||||||
|
|
||||||
|
// Send queried data to enclave over gRPC
|
||||||
|
let request = Request::new(RunClearingRequest {
|
||||||
|
message: json!(clearing_contents).to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut client = ClearingClient::connect(rpc_addr).await?;
|
||||||
|
let clearing_response = client
|
||||||
|
.run(request)
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!("Failed to communicate to relayer. {e}"))?
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
// Extract json from the Protobuf message
|
||||||
|
let quote: RawAttested<SubmitSetoffsMsg, Vec<u8>> =
|
||||||
|
serde_json::from_str(&clearing_response.message)
|
||||||
|
.map_err(|e| anyhow!("Error serializing SubmitSetoffs: {}", e))?;
|
||||||
|
|
||||||
|
// Get IAS report and build attested message
|
||||||
|
let attestation = gramine_ias_request(quote.attestation, user).await?;
|
||||||
|
let msg = RawAttestedMsgSansHandler(quote.msg);
|
||||||
|
|
||||||
|
let setoffs_msg =
|
||||||
|
ExecuteMsg::SubmitSetoffs::<EpidAttestation>(AttestedMsg { msg, attestation });
|
||||||
|
|
||||||
|
// Send setoffs to mtcs contract on chain
|
||||||
|
let output =
|
||||||
|
wasmd_client.tx_execute(contract, chain_id, 2000000, sender, json!(setoffs_msg))?;
|
||||||
|
|
||||||
|
println!("output: {}", output);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: replace raw queries with smart
|
||||||
|
async fn query_chain(
|
||||||
|
wasmd_client: &CliWasmdClient,
|
||||||
|
contract: &AccountId,
|
||||||
|
) -> Result<RunClearingMessage, anyhow::Error> {
|
||||||
|
// Get epoch counter
|
||||||
|
let resp: QueryResult<String> = wasmd_client
|
||||||
|
.query_raw(contract, hex::encode("epoch_counter"))
|
||||||
|
.map_err(|e| anyhow!("Problem querying epoch: {}", e))?;
|
||||||
|
let mut epoch_counter: usize =
|
||||||
|
String::from_utf8(BASE64_STANDARD.decode(resp.data)?)?.parse::<usize>()?;
|
||||||
|
if epoch_counter > 1 {
|
||||||
|
epoch_counter -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: replace with tracer log here
|
||||||
|
// println!("epoch: {}", epoch_counter);
|
||||||
|
|
||||||
|
// Get obligations
|
||||||
|
let resp: QueryResult<String> = wasmd_client
|
||||||
|
.query_raw(
|
||||||
|
contract,
|
||||||
|
hex::encode(format!("{}/obligations", epoch_counter)),
|
||||||
|
)
|
||||||
|
.map_err(|e| anyhow!("Problem querying obligatons: {}", e))?;
|
||||||
|
|
||||||
|
let decoded_obligs = BASE64_STANDARD.decode(resp.data)?;
|
||||||
|
let obligations_map: BTreeMap<HexBinary, HexBinary> =
|
||||||
|
serde_json::from_slice(&decoded_obligs).unwrap_or_default();
|
||||||
|
// println!("obligations \n {:?}", obligations_map);
|
||||||
|
// TODO: replace with tracer log here
|
||||||
|
|
||||||
|
// Get liquidity sources
|
||||||
|
let resp: QueryResult<GetLiquiditySourcesResponse> = wasmd_client
|
||||||
|
.query_smart(
|
||||||
|
contract,
|
||||||
|
json!(GetLiquiditySources {
|
||||||
|
epoch: Some(Uint64::new(epoch_counter as u64))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.map_err(|e| anyhow!("Problem querying liquidity sources: {}", e))?;
|
||||||
|
|
||||||
|
let liquidity_sources = resp.data.liquidity_sources;
|
||||||
|
// TODO: replace with tracer log here
|
||||||
|
// println!("liquidity_sources \n {:?}", liquidity_sources);
|
||||||
|
|
||||||
|
Ok(RunClearingMessage {
|
||||||
|
intents: obligations_map,
|
||||||
|
liquidity_sources: liquidity_sources.into_iter().collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request the IAS report for EPID attestations
|
||||||
|
async fn gramine_ias_request(
|
||||||
|
attested_msg: Vec<u8>,
|
||||||
|
user: &str,
|
||||||
|
) -> Result<EpidAttestation, anyhow::Error> {
|
||||||
|
let ias_api_key = String::from("669244b3e6364b5888289a11d2a1726d");
|
||||||
|
let ra_client_spid = String::from("51CAF5A48B450D624AEFE3286D314894");
|
||||||
|
let quote_file = format!("/tmp/{}_test.quote", user);
|
||||||
|
let report_file = format!("/tmp/{}_datareport", user);
|
||||||
|
let report_sig_file = format!("/tmp/{}_datareportsig", user);
|
||||||
|
|
||||||
|
// Write the binary data to a file
|
||||||
|
let mut file = File::create("e_file).await?;
|
||||||
|
file.write_all(&attested_msg)
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!("Couldn't write to file. {e}"))?;
|
||||||
|
|
||||||
|
let mut gramine = Command::new("gramine-sgx-ias-request");
|
||||||
|
let command = gramine
|
||||||
|
.arg("report")
|
||||||
|
.args(["-g", &ra_client_spid])
|
||||||
|
.args(["-k", &ias_api_key])
|
||||||
|
.args(["-q", "e_file])
|
||||||
|
.args(["-r", &report_file])
|
||||||
|
.args(["-s", &report_sig_file]);
|
||||||
|
|
||||||
|
let output = command.output()?;
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(anyhow!("Couldn't run gramine. {:?}", output));
|
||||||
|
}
|
||||||
|
|
||||||
|
let report: ReportBody = serde_json::from_str(&fs::read_to_string(report_file).await?)?;
|
||||||
|
let report_sig_str = fs::read_to_string(report_sig_file).await?.replace('\r', "");
|
||||||
|
let report_sig: Binary = Binary::from_base64(report_sig_str.trim())?;
|
||||||
|
|
||||||
|
Ok(EpidAttestation::new(IASReport { report, report_sig }))
|
||||||
|
}
|
3
apps/mtcs/scripts/src/lib.rs
Normal file
3
apps/mtcs/scripts/src/lib.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
//TODO: make an error.rs to differentiate errors in listen.rs
|
||||||
|
pub mod types;
|
||||||
|
pub mod utils;
|
39
apps/mtcs/scripts/src/types.rs
Normal file
39
apps/mtcs/scripts/src/types.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
// Rust libraries don't seem to implement this type from the wasmd go implementation
|
||||||
|
// TODO: Replace String with types from Rust libraries
|
||||||
|
// TODO: Move this into WasmdClient
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct WasmdTxResponse {
|
||||||
|
pub height: String,
|
||||||
|
pub txhash: String,
|
||||||
|
pub codespace: String,
|
||||||
|
pub code: u32,
|
||||||
|
pub data: String,
|
||||||
|
pub raw_log: String,
|
||||||
|
pub logs: Vec<serde_json::Value>,
|
||||||
|
pub info: String,
|
||||||
|
pub gas_wanted: String,
|
||||||
|
pub gas_used: String,
|
||||||
|
pub tx: Option<serde_json::Value>,
|
||||||
|
pub timestamp: String,
|
||||||
|
pub events: Vec<serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Attribute {
|
||||||
|
pub key: String,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Event {
|
||||||
|
pub attributes: Vec<Attribute>,
|
||||||
|
pub r#type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Log {
|
||||||
|
pub events: Vec<Event>,
|
||||||
|
pub msg_index: u32,
|
||||||
|
}
|
81
apps/mtcs/scripts/src/utils.rs
Normal file
81
apps/mtcs/scripts/src/utils.rs
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
use std::{path::Path, process::Command, time::Duration};
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use cosmrs::AccountId;
|
||||||
|
use regex::Regex;
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
use subtle_encoding::bech32::decode as bech32_decode;
|
||||||
|
use tendermint::Hash;
|
||||||
|
use tendermint_rpc::{
|
||||||
|
endpoint::tx::Response as TmTxResponse, error::ErrorDetail, Client, HttpClient,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn wasmaddr_to_id(address_str: &str) -> anyhow::Result<AccountId> {
|
||||||
|
let (hr, _) = bech32_decode(address_str).map_err(|e| anyhow!(e))?;
|
||||||
|
if hr != "wasm" {
|
||||||
|
return Err(anyhow!(hr));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(address_str.parse().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move wrapping result with "quartz:" struct into here
|
||||||
|
pub fn run_relay<R: DeserializeOwned>(
|
||||||
|
base_path: &Path,
|
||||||
|
msg: &str,
|
||||||
|
arg: Option<&str>,
|
||||||
|
) -> Result<R, anyhow::Error> {
|
||||||
|
let relayer_path = base_path.join("packages/relayer/scripts/relay.sh");
|
||||||
|
|
||||||
|
let mut bash = Command::new("bash");
|
||||||
|
let command = bash.arg(relayer_path).arg(msg);
|
||||||
|
|
||||||
|
if let Some(arg) = arg {
|
||||||
|
command.arg(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = command.output()?;
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(anyhow!("{:?}", output));
|
||||||
|
}
|
||||||
|
|
||||||
|
let query_result: R = serde_json::from_slice(&output.stdout)
|
||||||
|
.map_err(|e| anyhow!("Error deserializing: {}", e))?;
|
||||||
|
|
||||||
|
Ok(query_result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: time until tx commit is empiraclly 800ms on DO wasmd chain.
|
||||||
|
pub async fn block_tx_commit(client: &HttpClient, tx: Hash) -> Result<TmTxResponse, anyhow::Error> {
|
||||||
|
let re = Regex::new(r"tx \([A-F0-9]{64}\) not found").unwrap();
|
||||||
|
|
||||||
|
tokio::time::sleep(Duration::from_millis(400)).await;
|
||||||
|
loop {
|
||||||
|
match client.tx(tx, false).await {
|
||||||
|
Ok(resp) => {
|
||||||
|
return Ok(resp);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// If error, make sure it is only because of a not yet committed tx
|
||||||
|
match e.0 {
|
||||||
|
ErrorDetail::Response(subdetail) => {
|
||||||
|
if !re.is_match(subdetail.source.data().unwrap_or_default()) {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Error querying for tx: {}",
|
||||||
|
ErrorDetail::Response(subdetail)
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
println!("🔗 Waiting for tx commit... (+400ms)");
|
||||||
|
tokio::time::sleep(Duration::from_millis(400)).await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(anyhow!("Error querying for tx: {}", e.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
98
apps/mtcs/scripts/start.sh
Executable file
98
apps/mtcs/scripts/start.sh
Executable file
|
@ -0,0 +1,98 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#set -eo pipefail
|
||||||
|
|
||||||
|
ROOT=${ROOT:-$HOME}
|
||||||
|
DIR_QUARTZ="$ROOT/cycles-protocol"
|
||||||
|
DIR_QUARTZ_APP="$DIR_QUARTZ/quartz-app/"
|
||||||
|
DIR_QUARTZ_ENCLAVE="$DIR_QUARTZ/quartz-app/enclave"
|
||||||
|
DIR_QUARTZ_TM_PROVER="$DIR_QUARTZ/packages/tm-prover"
|
||||||
|
|
||||||
|
NODE_URL=${NODE_URL:-143.244.186.205:26657}
|
||||||
|
CMD="wasmd --node http://$NODE_URL"
|
||||||
|
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
echo "set trusted hash"
|
||||||
|
|
||||||
|
cd "$DIR_QUARTZ_TM_PROVER"
|
||||||
|
# cargo run -- --chain-id testing \
|
||||||
|
# --primary "http://$NODE_URL" \
|
||||||
|
# --witnesses "http://$NODE_URL" \
|
||||||
|
# --trusted-height 1 \
|
||||||
|
# --trusted-hash "5237772462A41C0296ED688A0327B8A60DF310F08997AD760EB74A70D0176C27" \
|
||||||
|
# --contract-address "wasm14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s0phg4d" \
|
||||||
|
# --storage-key "quartz_session" \
|
||||||
|
# --trace-file light-client-proof.json &> $DIR_QUARTZ_APP/output
|
||||||
|
|
||||||
|
# # Debug output of cargo run
|
||||||
|
# echo "Cargo run output:"
|
||||||
|
# cat $DIR_QUARTZ_APP/output
|
||||||
|
|
||||||
|
# cd $DIR_QUARTZ_APP
|
||||||
|
# # Debug hash extraction
|
||||||
|
# echo "Attempting to extract trusted hash from output..."
|
||||||
|
# cat output | grep found | head -1 | awk '{print $NF}' | sed 's/\x1b\[[0-9;]*m//g' > trusted.hash
|
||||||
|
|
||||||
|
# # Check if the hash was extracted correctly
|
||||||
|
# if [[ ! -s trusted.hash ]]; then
|
||||||
|
# echo "Failed to extract trusted hash from output"
|
||||||
|
# exit 1
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# export TRUSTED_HASH=$(cat trusted.hash)
|
||||||
|
# echo "Extracted TRUSTED_HASH: $TRUSTED_HASH"
|
||||||
|
# rm output
|
||||||
|
CHAIN_STATUS=$($CMD status)
|
||||||
|
TRUSTED_HASH=$(echo "$CHAIN_STATUS" | jq -r .SyncInfo.latest_block_hash)
|
||||||
|
TRUSTED_HEIGHT=$(echo "$CHAIN_STATUS" | jq -r .SyncInfo.latest_block_height)
|
||||||
|
echo "... $TRUSTED_HASH"
|
||||||
|
|
||||||
|
|
||||||
|
cd "$DIR_QUARTZ_APP"
|
||||||
|
echo "$TRUSTED_HASH" > trusted.hash
|
||||||
|
echo "$TRUSTED_HEIGHT" > trusted.height
|
||||||
|
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
echo "configure gramine"
|
||||||
|
cd "$DIR_QUARTZ_ENCLAVE"
|
||||||
|
|
||||||
|
echo "... gen priv key if it doesnt exist"
|
||||||
|
gramine-sgx-gen-private-key > /dev/null 2>&1 || : # may fail
|
||||||
|
|
||||||
|
# echo "... update manifest template with trusted hash $TRUSTED_HASH"
|
||||||
|
# sed -i -r "s/(\"--trusted-hash\", \")[A-Z0-9]+(\"])/\1$TRUSTED_HASH\2/" quartz.manifest.template
|
||||||
|
|
||||||
|
echo "... create manifest"
|
||||||
|
gramine-manifest \
|
||||||
|
-Dlog_level="error" \
|
||||||
|
-Dhome="$HOME" \
|
||||||
|
-Darch_libdir="/lib/$(gcc -dumpmachine)" \
|
||||||
|
-Dra_type="epid" \
|
||||||
|
-Dra_client_spid="51CAF5A48B450D624AEFE3286D314894" \
|
||||||
|
-Dra_client_linkable=1 \
|
||||||
|
-Dquartz_dir="$(pwd)" \
|
||||||
|
-Dtrusted_height="$TRUSTED_HEIGHT" \
|
||||||
|
-Dtrusted_hash="$TRUSTED_HASH" \
|
||||||
|
quartz.manifest.template quartz.manifest
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "gramine-manifest failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "... sign manifest"
|
||||||
|
gramine-sgx-sign --manifest quartz.manifest --output quartz.manifest.sgx
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "gramine-sgx-sign failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
echo "... start gramine"
|
||||||
|
gramine-sgx ./quartz
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "gramine-sgx failed to start"
|
||||||
|
exit 1
|
||||||
|
fi
|
128
apps/mtcs/scripts/start45.sh
Executable file
128
apps/mtcs/scripts/start45.sh
Executable file
|
@ -0,0 +1,128 @@
|
||||||
|
ROOT=${ROOT:-$HOME}
|
||||||
|
DIR_QUARTZ="$ROOT/cycles-protocol"
|
||||||
|
DIR_QUARTZ_APP="$DIR_QUARTZ/quartz-app/contracts/cw-tee-mtcs"
|
||||||
|
DIR_QUARTZ_ENCLAVE="$DIR_QUARTZ/quartz-app/enclave"
|
||||||
|
DIR_QUARTZ_TM_PROVER="$DIR_QUARTZ/packages/tm-prover"
|
||||||
|
|
||||||
|
NODE_URL=${NODE_URL:-143.244.186.205:36657}
|
||||||
|
CMD="wasmd --node http://$NODE_URL"
|
||||||
|
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
echo "set trusted hash"
|
||||||
|
|
||||||
|
cd "$DIR_QUARTZ_TM_PROVER"
|
||||||
|
cargo run -- --chain-id testing \
|
||||||
|
--primary "http://$NODE_URL" \
|
||||||
|
--witnesses "http://$NODE_URL" \
|
||||||
|
--trusted-height 500000 \
|
||||||
|
--trusted-hash "5237772462A41C0296ED688A0327B8A60DF310F08997AD760EB74A70D0176C27" \
|
||||||
|
--contract-address "wasm14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s0phg4d" \
|
||||||
|
--storage-key "quartz_session" \
|
||||||
|
--trace-file light-client-proof.json &> $DIR_QUARTZ_APP/output
|
||||||
|
|
||||||
|
|
||||||
|
cd $DIR_QUARTZ_APP
|
||||||
|
cat output | grep found | head -1 | awk '{print $NF}' | sed 's/\x1b\[[0-9;]*m//g' > trusted.hash
|
||||||
|
export TRUSTED_HASH=$(cat trusted.hash)
|
||||||
|
echo "... $TRUSTED_HASH"
|
||||||
|
rm output
|
||||||
|
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
echo "configure gramine"
|
||||||
|
cd "$DIR_QUARTZ_ENCLAVE"
|
||||||
|
|
||||||
|
echo "... gen priv key if it doesnt exist"
|
||||||
|
gramine-sgx-gen-private-key > /dev/null 2>&1 || : # may fail
|
||||||
|
|
||||||
|
echo "... update manifest template with trusted hash $TRUSTED_HASH"
|
||||||
|
sed -i -r "s/(\"--trusted-hash\", \")[A-Z0-9]+(\"])/\1$TRUSTED_HASH\2/" quartz.manifest.template
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
echo "... create manifest"
|
||||||
|
gramine-manifest \
|
||||||
|
-Dlog_level="error" \
|
||||||
|
-Dhome="$HOME" \
|
||||||
|
-Darch_libdir="/lib/$(gcc -dumpmachine)" \
|
||||||
|
-Dra_type="epid" \
|
||||||
|
-Dra_client_spid="51CAF5A48B450D624AEFE3286D314894" \
|
||||||
|
-Dra_client_linkable=1 \
|
||||||
|
-Dquartz_dir="$(pwd)" \
|
||||||
|
-Dtrusted_height="$TRUSTED_HEIGHT" \
|
||||||
|
-Dtrusted_hash="$TRUSTED_HASH" \
|
||||||
|
quartz.manifest.template quartz.manifest
|
||||||
|
|
||||||
|
echo "... sign manifest"
|
||||||
|
gramine-sgx-sign --manifest quartz.manifest --output quartz.manifest.sgx
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
echo "... start gramine"
|
||||||
|
gramine-sgx ./quartz
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# #set -eo pipefail
|
||||||
|
|
||||||
|
# ROOT=${ROOT:-$HOME}
|
||||||
|
# DIR_QUARTZ="$ROOT/quartz-app"
|
||||||
|
# DIR_QUARTZ_APP="$DIR_QUARTZ/contracts/cw-tee-mtcs"
|
||||||
|
# DIR_QUARTZ_ENCLAVE="$DIR_QUARTZ/enclave"
|
||||||
|
# DIR_QUARTZ_TM_PROVER="$DIR_QUARTZ/packages/tm-prover"
|
||||||
|
|
||||||
|
# NODE_URL=${NODE_URL:-143.244.186.205:36657}
|
||||||
|
# CMD="wasmd --node http://$NODE_URL"
|
||||||
|
|
||||||
|
|
||||||
|
# echo "--------------------------------------------------------"
|
||||||
|
# echo "set trusted hash"
|
||||||
|
|
||||||
|
# cd "$DIR_QUARTZ_TM_PROVER"
|
||||||
|
# cargo run -- --chain-id testing \
|
||||||
|
# --primary "http://$NODE_URL" \
|
||||||
|
# --witnesses "http://$NODE_URL" \
|
||||||
|
# --trusted-height 500000 \
|
||||||
|
# --trusted-hash "5237772462A41C0296ED688A0327B8A60DF310F08997AD760EB74A70D0176C27" \
|
||||||
|
# --contract-address "wasm14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s0phg4d" \
|
||||||
|
# --storage-key "quartz_session" \
|
||||||
|
# --trace-file light-client-proof.json &> $DIR_QUARTZ_APP/output
|
||||||
|
|
||||||
|
# cd $DIR_QUARTZ_APP
|
||||||
|
# cat output | grep found | head -1 | awk '{print $NF}' | sed 's/\x1b\[[0-9;]*m//g' > trusted.hash
|
||||||
|
# export TRUSTED_HASH=$(cat trusted.hash)
|
||||||
|
# echo "... $TRUSTED_HASH"
|
||||||
|
# rm output
|
||||||
|
|
||||||
|
# echo "--------------------------------------------------------"
|
||||||
|
# echo "configure gramine"
|
||||||
|
# cd "$DIR_QUARTZ_ENCLAVE"
|
||||||
|
|
||||||
|
# echo "... gen priv key if it doesnt exist"
|
||||||
|
# gramine-sgx-gen-private-key > /dev/null 2>&1 || : # may fail
|
||||||
|
|
||||||
|
# echo "... update manifest template with trusted hash $TRUSTED_HASH"
|
||||||
|
# sed -i -r "s/(\"--trusted-hash\", \")[A-Z0-9]+(\"])/\1$TRUSTED_HASH\2/" quartz.manifest.template
|
||||||
|
|
||||||
|
|
||||||
|
# echo "... create manifest"
|
||||||
|
# gramine-manifest \
|
||||||
|
# -Dlog_level="error" \
|
||||||
|
# -Dhome="$HOME" \
|
||||||
|
# -Darch_libdir="/lib/$(gcc -dumpmachine)" \
|
||||||
|
# -Dra_type="epid" \
|
||||||
|
# -Dra_client_spid="51CAF5A48B450D624AEFE3286D314894" \
|
||||||
|
# -Dra_client_linkable=1 \
|
||||||
|
# -Dquartz_dir="$(pwd)" \
|
||||||
|
# -Dtrusted_height="$TRUSTED_HEIGHT" \
|
||||||
|
# -Dtrusted_hash="$TRUSTED_HASH" \
|
||||||
|
# quartz.manifest.template quartz.manifest
|
||||||
|
|
||||||
|
# echo "... sign manifest"
|
||||||
|
# gramine-sgx-sign --manifest quartz.manifest --output quartz.manifest.sgx
|
||||||
|
|
||||||
|
|
||||||
|
# echo "--------------------------------------------------------"
|
||||||
|
# echo "... start gramine"
|
||||||
|
# gramine-sgx ./quartz
|
40
apps/mtcs/scripts/transact.sh
Normal file
40
apps/mtcs/scripts/transact.sh
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
|
||||||
|
OVERDRAFT=wasm1huhuswjxfydydxvdadqqsaet2p72wshtmr72yzx09zxncxtndf2sqs24hk
|
||||||
|
CMD='wasmd --node http://$NODE_URL'
|
||||||
|
|
||||||
|
# users
|
||||||
|
|
||||||
|
ALICE=wasm124tuy67a9dcvfgcr4gjmz60syd8ddaugl33v0n
|
||||||
|
BOB=wasm1ctkqmg45u85jnf5ur9796h7ze4hj6ep5y7m7l6
|
||||||
|
|
||||||
|
# query alice
|
||||||
|
|
||||||
|
$CMD query wasm contract-state smart $OVERDRAFT '{"balance": {"user": "'$ALICE'"}}'
|
||||||
|
|
||||||
|
# query bob
|
||||||
|
|
||||||
|
$CMD query wasm contract-state smart $OVERDRAFT '{"balance": {"user": "'$BOB'"}}'
|
||||||
|
|
||||||
|
# make obligation from alice to bob for 10
|
||||||
|
|
||||||
|
# $CMD tx wasm execute $CONTRACT '{"submit_obligation_msg": {"ciphertext": "", "digest": ""}}' --from $CONTRACT --chain-id testing
|
||||||
|
|
||||||
|
# make bob acceptance to overdraft for 10
|
||||||
|
|
||||||
|
# make alice tender from overdraft for 10
|
||||||
|
|
||||||
|
# init clearing
|
||||||
|
|
||||||
|
$CMD tx wasm execute $CONTRACT '"init_clearing"' --from $CONTRACT --chain-id testing
|
||||||
|
|
||||||
|
# wait for 2 sec
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# query alice
|
||||||
|
|
||||||
|
$CMD query wasm contract-state smart $OVERDRAFT '{"balance": {"user": "'$ALICE'"}}'
|
||||||
|
|
||||||
|
# query bob
|
||||||
|
|
||||||
|
$CMD query wasm contract-state smart $OVERDRAFT '{"balance": {"user": "'$BOB'"}}'
|
||||||
|
|
676
apps/transfers/contracts/Cargo.lock
generated
676
apps/transfers/contracts/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -24,7 +24,8 @@ incremental = false
|
||||||
overflow-checks = true
|
overflow-checks = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
mock-sgx = ["quartz-cw/mock-sgx"]
|
library = []
|
||||||
|
mock-sgx = ["quartz-common/mock-sgx-cw"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# external
|
# external
|
||||||
|
@ -42,7 +43,4 @@ cw20-base = { version = "2.0.0", features = ["library"] }
|
||||||
cw2 = "2.0.0"
|
cw2 = "2.0.0"
|
||||||
|
|
||||||
# quartz
|
# quartz
|
||||||
quartz-cw = { path = "../../../cosmwasm/packages/quartz-cw" }
|
quartz-common = { path = "../../../core/quartz-common", features=["contract"]}
|
||||||
|
|
||||||
# patch indirect deps
|
|
||||||
getrandom = { version = "0.2.15", features = ["js"] }
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use cosmwasm_std::{
|
||||||
entry_point, to_json_binary, Binary, Deps, DepsMut, Env, HexBinary, MessageInfo, Response,
|
entry_point, to_json_binary, Binary, Deps, DepsMut, Env, HexBinary, MessageInfo, Response,
|
||||||
StdResult,
|
StdResult,
|
||||||
};
|
};
|
||||||
use quartz_cw::handler::RawHandler;
|
use quartz_common::contract::handler::RawHandler;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::ContractError,
|
error::ContractError,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use cosmwasm_std::StdError;
|
use cosmwasm_std::StdError;
|
||||||
use cw20_base::ContractError as Cw20ContractError;
|
use cw20_base::ContractError as Cw20ContractError;
|
||||||
use cw_utils::PaymentError;
|
use cw_utils::PaymentError;
|
||||||
use quartz_cw::error::Error as QuartzError;
|
use quartz_common::contract::error::Error as QuartzError;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use cosmwasm_schema::cw_serde;
|
use cosmwasm_schema::cw_serde;
|
||||||
use quartz_cw::{
|
use quartz_common::contract::{
|
||||||
msg::execute::attested::{RawAttested, RawAttestedMsgSansHandler, RawDefaultAttestation},
|
msg::execute::attested::{RawAttested, RawAttestedMsgSansHandler, RawDefaultAttestation},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
type AttestedMsg<M, RA = RawDefaultAttestation> = RawAttested<RawAttestedMsgSansHandler<M>, RA>;
|
type AttestedMsg<M, RA = RawDefaultAttestation> = RawAttested<RawAttestedMsgSansHandler<M>, RA>;
|
||||||
|
|
||||||
|
@ -41,7 +40,7 @@ pub enum ExecuteMsg<RA = RawDefaultAttestation> {
|
||||||
pub mod execute {
|
pub mod execute {
|
||||||
use cosmwasm_schema::cw_serde;
|
use cosmwasm_schema::cw_serde;
|
||||||
use cosmwasm_std::{Addr, HexBinary, Uint128};
|
use cosmwasm_std::{Addr, HexBinary, Uint128};
|
||||||
use quartz_cw::{msg::execute::attested::HasUserData, state::UserData};
|
use quartz_common::contract::{msg::execute::attested::HasUserData, state::UserData};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
|
|
|
@ -10,7 +10,7 @@ name = "encrypt"
|
||||||
path = "bin/encrypt.rs"
|
path = "bin/encrypt.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
mock-sgx = ["quartz-cw/mock-sgx", "quartz-enclave/mock-sgx"]
|
mock-sgx = ["quartz-common/mock-sgx-cw", "quartz-common/mock-sgx-enclave"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# external
|
# external
|
||||||
|
@ -34,12 +34,15 @@ cosmwasm-std.workspace = true
|
||||||
tendermint.workspace = true
|
tendermint.workspace = true
|
||||||
tendermint-light-client.workspace = true
|
tendermint-light-client.workspace = true
|
||||||
|
|
||||||
# quartz
|
|
||||||
cycles-sync.workspace = true
|
cycles-sync.workspace = true
|
||||||
quartz-cw.workspace = true
|
|
||||||
quartz-proto.workspace = true
|
|
||||||
quartz-enclave.workspace = true
|
|
||||||
transfers-contract.workspace = true
|
transfers-contract.workspace = true
|
||||||
|
|
||||||
|
# quartz
|
||||||
|
quartz-common = { workspace=true, features=["full"]}
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
cw-multi-test = "0.17.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build.workspace = true
|
tonic-build.workspace = true
|
||||||
|
|
|
@ -25,12 +25,14 @@ use std::{
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use cli::Cli;
|
use cli::Cli;
|
||||||
use proto::settlement_server::SettlementServer as TransfersServer;
|
use proto::settlement_server::SettlementServer as TransfersServer;
|
||||||
use quartz_cw::state::{Config, LightClientOpts};
|
use quartz_common::{
|
||||||
use quartz_enclave::{
|
contract::state::{Config, LightClientOpts},
|
||||||
attestor::{Attestor, DefaultAttestor},
|
enclave::{
|
||||||
server::CoreService,
|
attestor::{Attestor, DefaultAttestor},
|
||||||
|
server::CoreService,
|
||||||
|
},
|
||||||
|
proto::core_server::CoreServer,
|
||||||
};
|
};
|
||||||
use quartz_proto::quartz::core_server::CoreServer;
|
|
||||||
use tonic::transport::Server;
|
use tonic::transport::Server;
|
||||||
use transfers_server::TransfersService;
|
use transfers_server::TransfersService;
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,13 @@ use std::{
|
||||||
use cosmwasm_std::{Addr, HexBinary, Uint128};
|
use cosmwasm_std::{Addr, HexBinary, Uint128};
|
||||||
use ecies::{decrypt, encrypt};
|
use ecies::{decrypt, encrypt};
|
||||||
use k256::ecdsa::{SigningKey, VerifyingKey};
|
use k256::ecdsa::{SigningKey, VerifyingKey};
|
||||||
use quartz_cw::{
|
use quartz_common::{
|
||||||
msg::execute::attested::{HasUserData, RawAttested},
|
contract::{
|
||||||
state::{Config, UserData},
|
msg::execute::attested::{HasUserData, RawAttested},
|
||||||
|
state::{Config, UserData},
|
||||||
|
},
|
||||||
|
enclave::{attestor::Attestor, server::ProofOfPublication},
|
||||||
};
|
};
|
||||||
use quartz_enclave::{attestor::Attestor, server::ProofOfPublication};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use tonic::{Request, Response, Result as TonicResult, Status};
|
use tonic::{Request, Response, Result as TonicResult, Status};
|
||||||
|
|
22
core/quartz-common/Cargo.toml
Normal file
22
core/quartz-common/Cargo.toml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
[package]
|
||||||
|
name = "quartz-common"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
readme.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
full = ["contract", "enclave", "proto"]
|
||||||
|
contract = ["dep:quartz-cw"]
|
||||||
|
enclave = ["dep:quartz-enclave", "proto"]
|
||||||
|
proto = ["dep:quartz-proto"]
|
||||||
|
mock-sgx-cw = ["quartz-cw/mock-sgx"]
|
||||||
|
mock-sgx-enclave = ["quartz-enclave/mock-sgx"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
quartz-cw = { workspace = true, optional = true }
|
||||||
|
quartz-proto = { workspace = true, optional = true }
|
||||||
|
quartz-enclave = { workspace = true, optional = true }
|
6
core/quartz-common/src/lib.rs
Normal file
6
core/quartz-common/src/lib.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#[cfg(feature = "contract")]
|
||||||
|
pub use quartz_cw as contract;
|
||||||
|
#[cfg(feature = "enclave")]
|
||||||
|
pub use quartz_enclave as enclave;
|
||||||
|
#[cfg(feature = "proto")]
|
||||||
|
pub use quartz_proto::quartz as proto;
|
|
@ -33,8 +33,6 @@ tendermint-light-client.workspace = true
|
||||||
|
|
||||||
# quartz
|
# quartz
|
||||||
cw-proof.workspace = true
|
cw-proof.workspace = true
|
||||||
cw-tee-mtcs.workspace = true
|
|
||||||
cycles-sync.workspace = true
|
|
||||||
mtcs.workspace = true
|
mtcs.workspace = true
|
||||||
quartz-cw.workspace = true
|
quartz-cw.workspace = true
|
||||||
quartz-proto.workspace = true
|
quartz-proto.workspace = true
|
||||||
|
|
|
@ -3,6 +3,7 @@ use std::{convert::Into, default::Default};
|
||||||
use cosmwasm_schema::cw_serde;
|
use cosmwasm_schema::cw_serde;
|
||||||
use cosmwasm_std::{HexBinary, StdError};
|
use cosmwasm_std::{HexBinary, StdError};
|
||||||
use quartz_tee_ra::IASReport;
|
use quartz_tee_ra::IASReport;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
#[cfg(not(feature = "mock-sgx"))]
|
#[cfg(not(feature = "mock-sgx"))]
|
||||||
pub type DefaultAttestation = EpidAttestation;
|
pub type DefaultAttestation = EpidAttestation;
|
||||||
|
@ -90,7 +91,7 @@ pub trait HasUserData {
|
||||||
fn user_data(&self) -> UserData;
|
fn user_data(&self) -> UserData;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||||
pub struct EpidAttestation {
|
pub struct EpidAttestation {
|
||||||
report: IASReport,
|
report: IASReport,
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,12 @@ pub struct RawSessionSetPubKey {
|
||||||
pub_key: HexBinary,
|
pub_key: HexBinary,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RawSessionSetPubKey {
|
||||||
|
pub fn pub_key(&self) -> &HexBinary {
|
||||||
|
&self.pub_key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<RawSessionSetPubKey> for SessionSetPubKey {
|
impl TryFrom<RawSessionSetPubKey> for SessionSetPubKey {
|
||||||
type Error = StdError;
|
type Error = StdError;
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,9 @@ REQUEST_MSG=${2:-"{}"}
|
||||||
# Use the QUARTZ_PORT environment variable if set, otherwise default to 11090
|
# Use the QUARTZ_PORT environment variable if set, otherwise default to 11090
|
||||||
QUARTZ_PORT="${QUARTZ_PORT:-11090}"
|
QUARTZ_PORT="${QUARTZ_PORT:-11090}"
|
||||||
|
|
||||||
|
# clear tmp files from previous runs
|
||||||
|
rm -f "$QUOTE_FILE" "$REPORT_FILE" "$REPORT_SIG_FILE"
|
||||||
|
|
||||||
# query the gRPC quartz enclave service
|
# query the gRPC quartz enclave service
|
||||||
ATTESTED_MSG=$(grpcurl -plaintext -import-path "$DIR_PROTO" -proto quartz.proto -d "$REQUEST_MSG" "127.0.0.1:$QUARTZ_PORT" quartz.Core/"$REQUEST" | jq -c '.message | fromjson')
|
ATTESTED_MSG=$(grpcurl -plaintext -import-path "$DIR_PROTO" -proto quartz.proto -d "$REQUEST_MSG" "127.0.0.1:$QUARTZ_PORT" quartz.Core/"$REQUEST" | jq -c '.message | fromjson')
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,13 @@ repository.workspace = true
|
||||||
keywords = ["blockchain", "cosmos", "tendermint", "cycles", "quartz"]
|
keywords = ["blockchain", "cosmos", "tendermint", "cycles", "quartz"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "submit"
|
||||||
|
path = "src/bin/submit.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# external
|
# external
|
||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
|
@ -26,10 +33,14 @@ tokio.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
tracing-subscriber.workspace = true
|
tracing-subscriber.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
|
anyhow ={ version = "*"}
|
||||||
|
|
||||||
# cosmos
|
# cosmos
|
||||||
cosmrs.workspace = true
|
cosmrs.workspace = true
|
||||||
cosmwasm-std.workspace = true
|
cosmwasm-std.workspace = true
|
||||||
|
|
||||||
|
# quartz
|
||||||
|
cw-tee-mtcs = { path = "../../apps/mtcs/contracts/cw-tee-mtcs/" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand_core.workspace = true
|
rand_core.workspace = true
|
||||||
|
|
174
utils/cycles-sync/src/bin/submit.rs
Normal file
174
utils/cycles-sync/src/bin/submit.rs
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use bip32::secp256k1::{
|
||||||
|
ecdsa::VerifyingKey,
|
||||||
|
sha2::{Digest, Sha256},
|
||||||
|
};
|
||||||
|
use clap::Parser;
|
||||||
|
use cosmrs::{tendermint::chain::Id as TmChainId, AccountId};
|
||||||
|
use cosmwasm_std::{Addr, HexBinary};
|
||||||
|
use cw_tee_mtcs::state::{LiquiditySource, LiquiditySourceType};
|
||||||
|
use cycles_sync::{
|
||||||
|
types::{
|
||||||
|
ContractObligation, RawEncryptedObligation, RawSetOff, SubmitObligationsMsg,
|
||||||
|
SubmitObligationsMsgInner,
|
||||||
|
},
|
||||||
|
wasmd_client::{CliWasmdClient, WasmdClient},
|
||||||
|
};
|
||||||
|
use reqwest::Url;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use subtle_encoding::bech32::decode as bech32_decode;
|
||||||
|
|
||||||
|
// const MNEMONIC_PHRASE: &str = "clutch debate vintage foster barely primary clown leader sell manual leopard ladder wet must embody story oyster imitate cable alien six square rice wedding";
|
||||||
|
|
||||||
|
const ADDRESS_PREFIX: &str = "wasm";
|
||||||
|
|
||||||
|
type Sha256Digest = [u8; 32];
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
struct QueryAllSetoffsResponse {
|
||||||
|
setoffs: Vec<(HexBinary, RawSetOff)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Parser)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct Cli {
|
||||||
|
#[arg(short, long, value_parser = wasmaddr_to_id)]
|
||||||
|
mtcs: AccountId,
|
||||||
|
|
||||||
|
#[arg(short, long)]
|
||||||
|
epoch_pk: String,
|
||||||
|
|
||||||
|
#[arg(short, long)]
|
||||||
|
overdraft: String,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
flip: bool,
|
||||||
|
|
||||||
|
#[arg(
|
||||||
|
short,
|
||||||
|
long,
|
||||||
|
default_value = "wasm14qdftsfk6fwn40l0xmruga08xlczl4g05npy70"
|
||||||
|
)]
|
||||||
|
admin: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), anyhow::Error> {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
let mut alice = Addr::unchecked("wasm124tuy67a9dcvfgcr4gjmz60syd8ddaugl33v0n");
|
||||||
|
let mut bob = Addr::unchecked("wasm1ctkqmg45u85jnf5ur9796h7ze4hj6ep5y7m7l6");
|
||||||
|
let overdraft = Addr::unchecked(cli.overdraft);
|
||||||
|
|
||||||
|
if cli.flip {
|
||||||
|
let temp = alice.clone();
|
||||||
|
alice = bob;
|
||||||
|
bob = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
let alice_to_bob: ContractObligation = ContractObligation {
|
||||||
|
debtor: alice.clone(),
|
||||||
|
creditor: bob.clone(),
|
||||||
|
amount: 10,
|
||||||
|
salt: HexBinary::from([0; 64]),
|
||||||
|
};
|
||||||
|
|
||||||
|
let bob_acceptance: ContractObligation = ContractObligation {
|
||||||
|
debtor: bob.clone(),
|
||||||
|
creditor: overdraft.clone(),
|
||||||
|
amount: 10,
|
||||||
|
salt: HexBinary::from([0; 64]),
|
||||||
|
};
|
||||||
|
|
||||||
|
let alice_tender: ContractObligation = ContractObligation {
|
||||||
|
debtor: overdraft.clone(),
|
||||||
|
creditor: alice.clone(),
|
||||||
|
amount: 10,
|
||||||
|
salt: HexBinary::from([0; 64]),
|
||||||
|
};
|
||||||
|
|
||||||
|
let intents = vec![alice_to_bob, bob_acceptance, alice_tender];
|
||||||
|
let epoch_pk = VerifyingKey::from_sec1_bytes(&hex::decode(cli.epoch_pk).unwrap()).unwrap();
|
||||||
|
|
||||||
|
let intents_enc = encrypt_overdraft_intents(intents, &epoch_pk);
|
||||||
|
|
||||||
|
let liquidity_sources: Vec<LiquiditySource> = vec![LiquiditySource {
|
||||||
|
address: overdraft,
|
||||||
|
source_type: LiquiditySourceType::Overdraft,
|
||||||
|
}];
|
||||||
|
|
||||||
|
let msg = create_wasm_msg(intents_enc, liquidity_sources)?;
|
||||||
|
|
||||||
|
let node_url = Url::parse("http://143.244.186.205:26657")?;
|
||||||
|
let chain_id = TmChainId::from_str("testing")?;
|
||||||
|
|
||||||
|
let wasmd_client = CliWasmdClient::new(node_url);
|
||||||
|
|
||||||
|
wasmd_client.tx_execute(&cli.mtcs, &chain_id, 3000000, cli.admin.to_string(), msg)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct OverdraftObligation {
|
||||||
|
pub debtor: Addr,
|
||||||
|
pub creditor: Addr,
|
||||||
|
pub amount: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encrypt_overdraft_intents(
|
||||||
|
intents: Vec<ContractObligation>,
|
||||||
|
epoch_pk: &VerifyingKey,
|
||||||
|
) -> Vec<(Sha256Digest, Vec<u8>)> {
|
||||||
|
let mut intents_enc = vec![];
|
||||||
|
|
||||||
|
for i in intents {
|
||||||
|
// serialize intent
|
||||||
|
let i_ser = serde_json::to_string(&i).unwrap();
|
||||||
|
|
||||||
|
// encrypt intent
|
||||||
|
let i_cipher = ecies::encrypt(&epoch_pk.to_sec1_bytes(), i_ser.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
// hash intent
|
||||||
|
let i_digest: Sha256Digest = {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(i_ser);
|
||||||
|
hasher.finalize().into()
|
||||||
|
};
|
||||||
|
|
||||||
|
intents_enc.push((i_digest, i_cipher));
|
||||||
|
}
|
||||||
|
|
||||||
|
intents_enc
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_wasm_msg(
|
||||||
|
obligations_enc: Vec<(Sha256Digest, Vec<u8>)>,
|
||||||
|
liquidity_sources: Vec<LiquiditySource>,
|
||||||
|
) -> anyhow::Result<serde_json::Value> {
|
||||||
|
let obligations_enc: Vec<_> = obligations_enc
|
||||||
|
.into_iter()
|
||||||
|
.map(|(digest, ciphertext)| {
|
||||||
|
let digest = HexBinary::from(digest);
|
||||||
|
let ciphertext = HexBinary::from(ciphertext);
|
||||||
|
RawEncryptedObligation { digest, ciphertext }
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let msg = SubmitObligationsMsg {
|
||||||
|
submit_obligations: SubmitObligationsMsgInner {
|
||||||
|
obligations: obligations_enc,
|
||||||
|
liquidity_sources,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
serde_json::to_value(msg).map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wasmaddr_to_id(address_str: &str) -> anyhow::Result<AccountId> {
|
||||||
|
let (hr, _) = bech32_decode(address_str).map_err(|e| anyhow!(e))?;
|
||||||
|
if hr != ADDRESS_PREFIX {
|
||||||
|
return Err(anyhow!(hr));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(address_str.parse().unwrap())
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ use subtle_encoding::{bech32::decode as bech32_decode, Error as Bech32DecodeErro
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::ADDRESS_PREFIX;
|
const ADDRESS_PREFIX: &str = "wasm";
|
||||||
|
|
||||||
#[derive(Clone, Debug, Parser)]
|
#[derive(Clone, Debug, Parser)]
|
||||||
#[command(author, version, about)]
|
#[command(author, version, about)]
|
||||||
|
|
|
@ -1 +1,4 @@
|
||||||
|
pub mod cli;
|
||||||
|
pub mod obligato_client;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
pub mod wasmd_client;
|
||||||
|
|
|
@ -27,7 +27,7 @@ use crate::{
|
||||||
obligato_client::{http::HttpClient, Client},
|
obligato_client::{http::HttpClient, Client},
|
||||||
types::{
|
types::{
|
||||||
Obligation, ObligatoObligation, ObligatoSetOff, RawEncryptedObligation, RawObligation,
|
Obligation, ObligatoObligation, ObligatoSetOff, RawEncryptedObligation, RawObligation,
|
||||||
RawOffset, RawSetOff, SubmitObligationsMsg, SubmitObligationsMsgInner,
|
RawOffset, RawSetOff, SubmitObligatioMsg, SubmitObligatoObligationsMsgInner,
|
||||||
},
|
},
|
||||||
wasmd_client::{CliWasmdClient, QueryResult, WasmdClient},
|
wasmd_client::{CliWasmdClient, QueryResult, WasmdClient},
|
||||||
};
|
};
|
||||||
|
@ -112,10 +112,8 @@ async fn sync_setoffs(cli: Cli) -> Result<(), DynError> {
|
||||||
.flat_map(|(obligation_digest, so)| match so {
|
.flat_map(|(obligation_digest, so)| match so {
|
||||||
RawSetOff::SetOff(sos_enc) => {
|
RawSetOff::SetOff(sos_enc) => {
|
||||||
let so_enc = sos_enc.first().unwrap();
|
let so_enc = sos_enc.first().unwrap();
|
||||||
let (debtor_id, creditor_id) = obligation_user_map
|
let (debtor_id, creditor_id) =
|
||||||
.get(obligation_digest)
|
obligation_user_map.get(obligation_digest).copied().unwrap();
|
||||||
.map(Clone::clone)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let sk = |id| keys[&id].private_key().to_bytes();
|
let sk = |id| keys[&id].private_key().to_bytes();
|
||||||
let so_ser = if let Ok(so) = ecies::decrypt(&sk(debtor_id), so_enc.as_slice()) {
|
let so_ser = if let Ok(so) = ecies::decrypt(&sk(debtor_id), so_enc.as_slice()) {
|
||||||
|
@ -202,8 +200,8 @@ fn create_wasm_msg(
|
||||||
.map(|pk| HexBinary::from(pk.to_sec1_bytes().as_ref()))
|
.map(|pk| HexBinary::from(pk.to_sec1_bytes().as_ref()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let msg = SubmitObligationsMsg {
|
let msg = SubmitObligatioMsg {
|
||||||
submit_obligations: SubmitObligationsMsgInner {
|
submit_obligations: SubmitObligatoObligationsMsgInner {
|
||||||
obligations: obligations_enc,
|
obligations: obligations_enc,
|
||||||
liquidity_sources,
|
liquidity_sources,
|
||||||
},
|
},
|
||||||
|
@ -377,7 +375,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_mnemonic() {
|
fn test_create_mnemonic() {
|
||||||
// Generate random Mnemonic using the default language (English)
|
// Generate random Mnemonic using the default language (English)
|
||||||
let mnemonic = Mnemonic::random(&mut OsRng, Default::default());
|
let mnemonic = Mnemonic::random(OsRng, Default::default());
|
||||||
println!("{}", mnemonic.phrase());
|
println!("{}", mnemonic.phrase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use bip32::secp256k1::ecdsa::VerifyingKey;
|
use bip32::secp256k1::ecdsa::VerifyingKey;
|
||||||
use cosmwasm_std::HexBinary;
|
use cosmwasm_std::{Addr, HexBinary};
|
||||||
|
use cw_tee_mtcs::state::LiquiditySource;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
@ -11,6 +12,15 @@ pub struct ObligatoObligation {
|
||||||
pub amount: u64,
|
pub amount: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ContractObligation {
|
||||||
|
pub debtor: Addr,
|
||||||
|
pub creditor: Addr,
|
||||||
|
pub amount: u64,
|
||||||
|
#[serde(default)]
|
||||||
|
pub salt: HexBinary,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct RawObligation {
|
pub struct RawObligation {
|
||||||
pub debtor: HexBinary,
|
pub debtor: HexBinary,
|
||||||
|
@ -52,6 +62,17 @@ pub struct SubmitObligationsMsg {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct SubmitObligationsMsgInner {
|
pub struct SubmitObligationsMsgInner {
|
||||||
|
pub obligations: Vec<RawEncryptedObligation>,
|
||||||
|
pub liquidity_sources: Vec<LiquiditySource>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct SubmitObligatioMsg {
|
||||||
|
pub submit_obligations: SubmitObligatoObligationsMsgInner,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct SubmitObligatoObligationsMsgInner {
|
||||||
pub obligations: Vec<RawEncryptedObligation>,
|
pub obligations: Vec<RawEncryptedObligation>,
|
||||||
pub liquidity_sources: Vec<HexBinary>,
|
pub liquidity_sources: Vec<HexBinary>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,30 @@
|
||||||
use std::{error::Error, process::Command};
|
use std::process::Command;
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
use cosmrs::{tendermint::chain::Id, AccountId};
|
use cosmrs::{tendermint::chain::Id, AccountId};
|
||||||
|
use hex::ToHex;
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
pub trait WasmdClient {
|
pub trait WasmdClient {
|
||||||
type Address: AsRef<str>;
|
type Address: AsRef<str>;
|
||||||
type Query: ToString;
|
type Query: ToString;
|
||||||
|
type RawQuery: ToHex;
|
||||||
type ChainId: AsRef<str>;
|
type ChainId: AsRef<str>;
|
||||||
type Error;
|
type Error;
|
||||||
|
|
||||||
fn query_smart<R: FromVec>(
|
fn query_smart<R: DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
contract: &Self::Address,
|
contract: &Self::Address,
|
||||||
query: Self::Query,
|
query: Self::Query,
|
||||||
) -> Result<R, Self::Error>;
|
) -> Result<R, Self::Error>;
|
||||||
|
|
||||||
|
fn query_raw<R: DeserializeOwned + Default>(
|
||||||
|
&self,
|
||||||
|
contract: &Self::Address,
|
||||||
|
query: Self::RawQuery,
|
||||||
|
) -> Result<R, Self::Error>;
|
||||||
|
|
||||||
fn tx_execute<M: ToString>(
|
fn tx_execute<M: ToString>(
|
||||||
&self,
|
&self,
|
||||||
contract: &Self::Address,
|
contract: &Self::Address,
|
||||||
|
@ -24,24 +32,30 @@ pub trait WasmdClient {
|
||||||
gas: u64,
|
gas: u64,
|
||||||
sender: String,
|
sender: String,
|
||||||
msg: M,
|
msg: M,
|
||||||
) -> Result<(), Self::Error>;
|
) -> Result<String, Self::Error>;
|
||||||
|
|
||||||
|
fn deploy<M: ToString>(
|
||||||
|
&self,
|
||||||
|
chain_id: &Id,
|
||||||
|
sender: String, // what should this type be
|
||||||
|
wasm_path: M,
|
||||||
|
) -> Result<String, Self::Error>;
|
||||||
|
|
||||||
|
fn init<M: ToString>(
|
||||||
|
&self,
|
||||||
|
chain_id: &Id,
|
||||||
|
sender: String,
|
||||||
|
code_id: usize,
|
||||||
|
init_msg: M,
|
||||||
|
label: String,
|
||||||
|
) -> Result<String, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
|
||||||
pub struct QueryResult<T> {
|
pub struct QueryResult<T> {
|
||||||
pub data: T,
|
pub data: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FromVec: Sized {
|
|
||||||
fn from_vec(value: Vec<u8>) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: for<'any> Deserialize<'any>> FromVec for T {
|
|
||||||
fn from_vec(value: Vec<u8>) -> Self {
|
|
||||||
serde_json::from_slice(&value).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CliWasmdClient {
|
pub struct CliWasmdClient {
|
||||||
url: Url,
|
url: Url,
|
||||||
|
@ -56,10 +70,11 @@ impl CliWasmdClient {
|
||||||
impl WasmdClient for CliWasmdClient {
|
impl WasmdClient for CliWasmdClient {
|
||||||
type Address = AccountId;
|
type Address = AccountId;
|
||||||
type Query = serde_json::Value;
|
type Query = serde_json::Value;
|
||||||
|
type RawQuery = String;
|
||||||
type ChainId = Id;
|
type ChainId = Id;
|
||||||
type Error = Box<dyn Error>;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
fn query_smart<R: FromVec>(
|
fn query_smart<R: DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
contract: &Self::Address,
|
contract: &Self::Address,
|
||||||
query: Self::Query,
|
query: Self::Query,
|
||||||
|
@ -73,9 +88,34 @@ impl WasmdClient for CliWasmdClient {
|
||||||
.args(["--output", "json"]);
|
.args(["--output", "json"]);
|
||||||
|
|
||||||
let output = command.output()?;
|
let output = command.output()?;
|
||||||
debug!("{:?} => {:?}", command, output);
|
if !output.status.success() {
|
||||||
|
return Err(anyhow!("{:?}", output));
|
||||||
|
}
|
||||||
|
|
||||||
let query_result = R::from_vec(output.stdout);
|
let query_result: R = serde_json::from_slice(&output.stdout)
|
||||||
|
.map_err(|e| anyhow!("Error deserializing: {}", e))?;
|
||||||
|
Ok(query_result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_raw<R: DeserializeOwned + Default>(
|
||||||
|
&self,
|
||||||
|
contract: &Self::Address,
|
||||||
|
query: Self::RawQuery,
|
||||||
|
) -> Result<R, Self::Error> {
|
||||||
|
let mut wasmd = Command::new("wasmd");
|
||||||
|
let command = wasmd
|
||||||
|
.args(["--node", self.url.as_str()])
|
||||||
|
.args(["query", "wasm"])
|
||||||
|
.args(["contract-state", "raw", contract.as_ref()])
|
||||||
|
.arg(&query)
|
||||||
|
.args(["--output", "json"]);
|
||||||
|
|
||||||
|
let output = command.output()?;
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(anyhow!("{:?}", output));
|
||||||
|
}
|
||||||
|
|
||||||
|
let query_result: R = serde_json::from_slice(&output.stdout).unwrap_or_default();
|
||||||
Ok(query_result)
|
Ok(query_result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +126,7 @@ impl WasmdClient for CliWasmdClient {
|
||||||
gas: u64,
|
gas: u64,
|
||||||
sender: String,
|
sender: String,
|
||||||
msg: M,
|
msg: M,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<String, Self::Error> {
|
||||||
let mut wasmd = Command::new("wasmd");
|
let mut wasmd = Command::new("wasmd");
|
||||||
let command = wasmd
|
let command = wasmd
|
||||||
.args(["--node", self.url.as_str()])
|
.args(["--node", self.url.as_str()])
|
||||||
|
@ -99,12 +139,73 @@ impl WasmdClient for CliWasmdClient {
|
||||||
.arg("-y");
|
.arg("-y");
|
||||||
|
|
||||||
let output = command.output()?;
|
let output = command.output()?;
|
||||||
debug!("{:?} => {:?}", command, output);
|
|
||||||
|
|
||||||
if output.status.success() {
|
if !output.status.success() {
|
||||||
println!("{}", String::from_utf8(output.stdout).unwrap());
|
return Err(anyhow!("{:?}", output));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
// TODO: find the rust type for the tx output and return that
|
||||||
|
Ok((String::from_utf8(output.stdout)?).to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deploy<M: ToString>(
|
||||||
|
&self,
|
||||||
|
chain_id: &Id,
|
||||||
|
sender: String,
|
||||||
|
wasm_path: M,
|
||||||
|
) -> Result<String, Self::Error> {
|
||||||
|
let mut wasmd = Command::new("wasmd");
|
||||||
|
let command = wasmd
|
||||||
|
.args(["--node", self.url.as_str()])
|
||||||
|
.args(["tx", "wasm", "store", &wasm_path.to_string()])
|
||||||
|
.args(["--from", sender.as_ref()])
|
||||||
|
.args(["--chain-id", chain_id.as_ref()])
|
||||||
|
.args(["--gas-prices", "0.0025ucosm"])
|
||||||
|
.args(["--gas", "auto"])
|
||||||
|
.args(["--gas-adjustment", "1.3"])
|
||||||
|
.args(["-o", "json"])
|
||||||
|
.arg("-y");
|
||||||
|
|
||||||
|
let output = command.output()?;
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(anyhow!("{:?}", output));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: find the rust type for the tx output and return that
|
||||||
|
Ok((String::from_utf8(output.stdout)?).to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init<M: ToString>(
|
||||||
|
&self,
|
||||||
|
chain_id: &Id,
|
||||||
|
sender: String,
|
||||||
|
code_id: usize,
|
||||||
|
init_msg: M,
|
||||||
|
label: String,
|
||||||
|
) -> Result<String, Self::Error> {
|
||||||
|
let mut wasmd = Command::new("wasmd");
|
||||||
|
let command = wasmd
|
||||||
|
.args(["--node", self.url.as_str()])
|
||||||
|
.args(["tx", "wasm", "instantiate"])
|
||||||
|
.args([&code_id.to_string(), &init_msg.to_string()])
|
||||||
|
.args(["--label", label.as_ref()])
|
||||||
|
.args(["--from", sender.as_ref()])
|
||||||
|
.arg("--no-admin")
|
||||||
|
.args(["--chain-id", chain_id.as_ref()])
|
||||||
|
.args(["--gas-prices", "0.0025ucosm"])
|
||||||
|
.args(["--gas", "auto"])
|
||||||
|
.args(["--gas-adjustment", "1.3"])
|
||||||
|
.args(["-o", "json"])
|
||||||
|
.arg("-y");
|
||||||
|
|
||||||
|
let output = command.output()?;
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(anyhow!("{:?}", output));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: find the rust type for the tx output and return that
|
||||||
|
Ok((String::from_utf8(output.stdout)?).to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,10 @@ repository.workspace = true
|
||||||
keywords = ["blockchain", "cosmos", "tendermint", "cycles", "quartz"]
|
keywords = ["blockchain", "cosmos", "tendermint", "cycles", "quartz"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "main"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# external
|
# external
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
|
|
147
utils/tm-prover/src/config.rs
Normal file
147
utils/tm-prover/src/config.rs
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
use std::{num::ParseIntError, path::PathBuf, str::FromStr};
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use color_eyre::eyre::{eyre, Result};
|
||||||
|
use cosmrs::AccountId;
|
||||||
|
use cw_proof::proof::cw::RawCwProof;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tendermint_light_client::types::{Hash, Height, LightBlock, TrustThreshold};
|
||||||
|
use tendermint_rpc::HttpClientUrl;
|
||||||
|
use tracing::metadata::LevelFilter;
|
||||||
|
|
||||||
|
pub fn parse_trust_threshold(s: &str) -> Result<TrustThreshold> {
|
||||||
|
if let Some((l, r)) = s.split_once('/') {
|
||||||
|
TrustThreshold::new(l.parse()?, r.parse()?).map_err(Into::into)
|
||||||
|
} else {
|
||||||
|
Err(eyre!(
|
||||||
|
"invalid trust threshold: {s}, format must be X/Y where X and Y are integers"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct List<T>(pub Vec<T>);
|
||||||
|
|
||||||
|
impl<E, T: FromStr<Err = E>> FromStr for List<T> {
|
||||||
|
type Err = E;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
s.split(',')
|
||||||
|
.map(|s| s.parse())
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
.map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(clap::Args, Debug, Clone)]
|
||||||
|
pub struct Verbosity {
|
||||||
|
/// Increase verbosity, can be repeated up to 2 times
|
||||||
|
#[arg(long, short, action = clap::ArgAction::Count)]
|
||||||
|
pub verbose: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Verbosity {
|
||||||
|
pub fn to_level_filter(&self) -> LevelFilter {
|
||||||
|
match self.verbose {
|
||||||
|
0 => LevelFilter::INFO,
|
||||||
|
1 => LevelFilter::DEBUG,
|
||||||
|
_ => LevelFilter::TRACE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { verbose: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Verbosity {
|
||||||
|
type Err = ParseIntError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let verbose: u8 = s.parse()?;
|
||||||
|
Ok(Self { verbose })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ProofOutput {
|
||||||
|
pub light_client_proof: Vec<LightBlock>,
|
||||||
|
pub merkle_proof: RawCwProof,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Investigate if it's possible to derive default using Clap's default values
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Config {
|
||||||
|
chain_id: String::default(),
|
||||||
|
primary: "http://127.0.0.1:26657".parse().unwrap(),
|
||||||
|
witnesses: "[]".parse().unwrap(),
|
||||||
|
trusted_height: Height::default(),
|
||||||
|
trusted_hash: Hash::default(),
|
||||||
|
trust_threshold: TrustThreshold::TWO_THIRDS,
|
||||||
|
trusting_period: 1209600u64,
|
||||||
|
max_clock_drift: 5u64,
|
||||||
|
max_block_lag: 5u64,
|
||||||
|
trace_file: None,
|
||||||
|
verbose: Verbosity::default(),
|
||||||
|
contract_address: "".parse().unwrap(),
|
||||||
|
storage_key: String::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
#[command(author, version, about, long_about = None)]
|
||||||
|
pub struct Config {
|
||||||
|
/// Identifier of the chain
|
||||||
|
#[clap(long)]
|
||||||
|
pub chain_id: String,
|
||||||
|
|
||||||
|
/// Primary RPC address
|
||||||
|
#[clap(long, default_value = "http://127.0.0.1:26657")]
|
||||||
|
pub primary: HttpClientUrl,
|
||||||
|
|
||||||
|
/// Comma-separated list of witnesses RPC addresses
|
||||||
|
#[clap(long)]
|
||||||
|
pub witnesses: List<HttpClientUrl>,
|
||||||
|
|
||||||
|
/// Height of trusted header
|
||||||
|
#[clap(long)]
|
||||||
|
pub trusted_height: Height,
|
||||||
|
|
||||||
|
/// Hash of trusted header
|
||||||
|
#[clap(long)]
|
||||||
|
pub trusted_hash: Hash,
|
||||||
|
|
||||||
|
/// Trust threshold
|
||||||
|
#[clap(long, value_parser = parse_trust_threshold, default_value_t = TrustThreshold::TWO_THIRDS)]
|
||||||
|
pub trust_threshold: TrustThreshold,
|
||||||
|
|
||||||
|
/// Trusting period, in seconds (default: two weeks)
|
||||||
|
#[clap(long, default_value = "1209600")]
|
||||||
|
pub trusting_period: u64,
|
||||||
|
|
||||||
|
/// Maximum clock drift, in seconds
|
||||||
|
#[clap(long, default_value = "5")]
|
||||||
|
pub max_clock_drift: u64,
|
||||||
|
|
||||||
|
/// Maximum block lag, in seconds
|
||||||
|
#[clap(long, default_value = "5")]
|
||||||
|
pub max_block_lag: u64,
|
||||||
|
|
||||||
|
/// Output file to store light client proof (AKA verification trace)
|
||||||
|
#[clap(long)]
|
||||||
|
pub trace_file: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Increase verbosity
|
||||||
|
#[clap(flatten)]
|
||||||
|
pub verbose: Verbosity,
|
||||||
|
|
||||||
|
/// Address of the CosmWasm contract
|
||||||
|
#[clap(long)]
|
||||||
|
pub contract_address: AccountId,
|
||||||
|
|
||||||
|
/// Storage key of the state item for which proofs must be retrieved
|
||||||
|
#[clap(long)]
|
||||||
|
pub storage_key: String,
|
||||||
|
}
|
2
utils/tm-prover/src/lib.rs
Normal file
2
utils/tm-prover/src/lib.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod config;
|
||||||
|
pub mod prover;
|
|
@ -1,361 +1,12 @@
|
||||||
#![deny(
|
|
||||||
warnings,
|
|
||||||
trivial_casts,
|
|
||||||
trivial_numeric_casts,
|
|
||||||
unused_import_braces,
|
|
||||||
unused_qualifications
|
|
||||||
)]
|
|
||||||
#![forbid(unsafe_code)]
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
fs::File,
|
|
||||||
io::{BufWriter, Write},
|
|
||||||
path::PathBuf,
|
|
||||||
str::FromStr,
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use color_eyre::{
|
use color_eyre::eyre::Result;
|
||||||
eyre::{eyre, Result},
|
use tm_prover::{config::Config, prover::prove};
|
||||||
Report,
|
|
||||||
};
|
|
||||||
use cosmrs::AccountId;
|
|
||||||
use cw_proof::{
|
|
||||||
error::ProofError,
|
|
||||||
proof::{
|
|
||||||
cw::{CwProof, RawCwProof},
|
|
||||||
key::CwAbciKey,
|
|
||||||
Proof,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use futures::future::join_all;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use tendermint::{crypto::default::Sha256, evidence::Evidence};
|
|
||||||
use tendermint_light_client::{
|
|
||||||
builder::LightClientBuilder,
|
|
||||||
light_client::Options,
|
|
||||||
store::memory::MemoryStore,
|
|
||||||
types::{Hash, Height, LightBlock, TrustThreshold},
|
|
||||||
};
|
|
||||||
use tendermint_light_client_detector::{detect_divergence, Error, Provider, Trace};
|
|
||||||
use tendermint_rpc::{client::HttpClient, Client, HttpClientUrl};
|
|
||||||
use tracing::{error, info, metadata::LevelFilter};
|
|
||||||
use tracing_subscriber::{util::SubscriberInitExt, EnvFilter};
|
|
||||||
|
|
||||||
const WASM_STORE_KEY: &str = "/store/wasm/key";
|
|
||||||
|
|
||||||
fn parse_trust_threshold(s: &str) -> Result<TrustThreshold> {
|
|
||||||
if let Some((l, r)) = s.split_once('/') {
|
|
||||||
TrustThreshold::new(l.parse()?, r.parse()?).map_err(Into::into)
|
|
||||||
} else {
|
|
||||||
Err(eyre!(
|
|
||||||
"invalid trust threshold: {s}, format must be X/Y where X and Y are integers"
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
struct List<T>(Vec<T>);
|
|
||||||
|
|
||||||
impl<E, T: FromStr<Err = E>> FromStr for List<T> {
|
|
||||||
type Err = E;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
s.split(',')
|
|
||||||
.map(|s| s.parse())
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map(Self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(clap::Args, Debug, Clone)]
|
|
||||||
struct Verbosity {
|
|
||||||
/// Increase verbosity, can be repeated up to 2 times
|
|
||||||
#[arg(long, short, action = clap::ArgAction::Count)]
|
|
||||||
verbose: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Verbosity {
|
|
||||||
fn to_level_filter(&self) -> LevelFilter {
|
|
||||||
match self.verbose {
|
|
||||||
0 => LevelFilter::INFO,
|
|
||||||
1 => LevelFilter::DEBUG,
|
|
||||||
_ => LevelFilter::TRACE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
struct ProofOutput {
|
|
||||||
light_client_proof: Vec<LightBlock>,
|
|
||||||
merkle_proof: RawCwProof,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
|
||||||
#[command(author, version, about, long_about = None)]
|
|
||||||
struct Cli {
|
|
||||||
/// Identifier of the chain
|
|
||||||
#[clap(long)]
|
|
||||||
chain_id: String,
|
|
||||||
|
|
||||||
/// Primary RPC address
|
|
||||||
#[clap(long, default_value = "http://127.0.0.1:26657")]
|
|
||||||
primary: HttpClientUrl,
|
|
||||||
|
|
||||||
/// Comma-separated list of witnesses RPC addresses
|
|
||||||
#[clap(long)]
|
|
||||||
witnesses: List<HttpClientUrl>,
|
|
||||||
|
|
||||||
/// Height of trusted header
|
|
||||||
#[clap(long)]
|
|
||||||
trusted_height: Height,
|
|
||||||
|
|
||||||
/// Hash of trusted header
|
|
||||||
#[clap(long)]
|
|
||||||
trusted_hash: Hash,
|
|
||||||
|
|
||||||
/// Trust threshold
|
|
||||||
#[clap(long, value_parser = parse_trust_threshold, default_value_t = TrustThreshold::TWO_THIRDS)]
|
|
||||||
trust_threshold: TrustThreshold,
|
|
||||||
|
|
||||||
/// Trusting period, in seconds (default: two weeks)
|
|
||||||
#[clap(long, default_value = "1209600")]
|
|
||||||
trusting_period: u64,
|
|
||||||
|
|
||||||
/// Maximum clock drift, in seconds
|
|
||||||
#[clap(long, default_value = "5")]
|
|
||||||
max_clock_drift: u64,
|
|
||||||
|
|
||||||
/// Maximum block lag, in seconds
|
|
||||||
#[clap(long, default_value = "5")]
|
|
||||||
max_block_lag: u64,
|
|
||||||
|
|
||||||
/// Output file to store light client proof (AKA verification trace)
|
|
||||||
#[clap(long)]
|
|
||||||
trace_file: Option<PathBuf>,
|
|
||||||
|
|
||||||
/// Increase verbosity
|
|
||||||
#[clap(flatten)]
|
|
||||||
verbose: Verbosity,
|
|
||||||
|
|
||||||
/// Address of the CosmWasm contract
|
|
||||||
#[clap(long)]
|
|
||||||
contract_address: AccountId,
|
|
||||||
|
|
||||||
/// Storage key of the state item for which proofs must be retrieved
|
|
||||||
#[clap(long)]
|
|
||||||
storage_key: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
|
|
||||||
let args = Cli::parse();
|
let args = Config::parse();
|
||||||
|
|
||||||
let env_filter = EnvFilter::builder()
|
prove(args).await
|
||||||
.with_default_directive(args.verbose.to_level_filter().into())
|
|
||||||
.from_env_lossy();
|
|
||||||
|
|
||||||
tracing_subscriber::fmt()
|
|
||||||
.with_target(false)
|
|
||||||
.with_env_filter(env_filter)
|
|
||||||
.finish()
|
|
||||||
.init();
|
|
||||||
|
|
||||||
let options = Options {
|
|
||||||
trust_threshold: args.trust_threshold,
|
|
||||||
trusting_period: Duration::from_secs(args.trusting_period),
|
|
||||||
clock_drift: Duration::from_secs(args.max_clock_drift),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut primary = make_provider(
|
|
||||||
&args.chain_id,
|
|
||||||
args.primary.clone(),
|
|
||||||
args.trusted_height,
|
|
||||||
args.trusted_hash,
|
|
||||||
options,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let client = HttpClient::builder(args.primary.clone()).build()?;
|
|
||||||
|
|
||||||
let trusted_block = primary
|
|
||||||
.latest_trusted()
|
|
||||||
.ok_or_else(|| eyre!("No trusted state found for primary"))?;
|
|
||||||
|
|
||||||
let status = client.status().await?;
|
|
||||||
let latest_height = status.sync_info.latest_block_height;
|
|
||||||
let latest_app_hash = status.sync_info.latest_app_hash;
|
|
||||||
|
|
||||||
// `proof_height` is the height at which we want to query the blockchain's state
|
|
||||||
// This is one less than than the `latest_height` because we want to verify the merkle-proof for
|
|
||||||
// the state against the `app_hash` at `latest_height`.
|
|
||||||
// (because Tendermint commits to the latest `app_hash` in the subsequent block)
|
|
||||||
let proof_height = (latest_height.value() - 1)
|
|
||||||
.try_into()
|
|
||||||
.expect("infallible conversion");
|
|
||||||
|
|
||||||
info!("Verifying to latest height on primary...");
|
|
||||||
|
|
||||||
let primary_block = primary.verify_to_height(latest_height)?;
|
|
||||||
|
|
||||||
info!("Verified to height {} on primary", primary_block.height());
|
|
||||||
let mut primary_trace = primary.get_trace(primary_block.height());
|
|
||||||
|
|
||||||
let witnesses = join_all(args.witnesses.0.into_iter().map(|addr| {
|
|
||||||
make_provider(
|
|
||||||
&args.chain_id,
|
|
||||||
addr,
|
|
||||||
trusted_block.height(),
|
|
||||||
trusted_block.signed_header.header.hash(),
|
|
||||||
options,
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let mut witnesses = witnesses.into_iter().collect::<Result<Vec<_>>>()?;
|
|
||||||
|
|
||||||
let max_clock_drift = Duration::from_secs(args.max_clock_drift);
|
|
||||||
let max_block_lag = Duration::from_secs(args.max_block_lag);
|
|
||||||
|
|
||||||
run_detector(
|
|
||||||
&mut primary,
|
|
||||||
witnesses.as_mut_slice(),
|
|
||||||
primary_trace.clone(),
|
|
||||||
max_clock_drift,
|
|
||||||
max_block_lag,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let path = WASM_STORE_KEY.to_owned();
|
|
||||||
let data = CwAbciKey::new(args.contract_address, args.storage_key, None);
|
|
||||||
let result = client
|
|
||||||
.abci_query(Some(path), data, Some(proof_height), true)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let proof: CwProof = result
|
|
||||||
.clone()
|
|
||||||
.try_into()
|
|
||||||
.expect("result should contain proof");
|
|
||||||
proof
|
|
||||||
.verify(latest_app_hash.clone().into())
|
|
||||||
.map_err(|e: ProofError| eyre!(e))?;
|
|
||||||
|
|
||||||
if let Some(trace_file) = args.trace_file {
|
|
||||||
// replace the last block in the trace (i.e. the (latest - 1) block) with the latest block
|
|
||||||
// we don't actually verify the latest block because it will be verified on the other side
|
|
||||||
let latest_block = primary.fetch_light_block(latest_height)?;
|
|
||||||
let _ = primary_trace.pop();
|
|
||||||
primary_trace.push(latest_block);
|
|
||||||
|
|
||||||
let output = ProofOutput {
|
|
||||||
light_client_proof: primary_trace,
|
|
||||||
merkle_proof: proof.into(),
|
|
||||||
};
|
|
||||||
write_proof_to_file(trace_file, output).await?;
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn write_proof_to_file(trace_file: PathBuf, output: ProofOutput) -> Result<()> {
|
|
||||||
info!("Writing proof to output file ({})", trace_file.display());
|
|
||||||
|
|
||||||
let file = File::create(trace_file)?;
|
|
||||||
let mut writer = BufWriter::new(file);
|
|
||||||
serde_json::to_writer(&mut writer, &output)?;
|
|
||||||
writer.flush()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run_detector(
|
|
||||||
primary: &mut Provider,
|
|
||||||
witnesses: &mut [Provider],
|
|
||||||
primary_trace: Vec<LightBlock>,
|
|
||||||
max_clock_drift: Duration,
|
|
||||||
max_block_lag: Duration,
|
|
||||||
) -> Result<(), Report> {
|
|
||||||
if witnesses.is_empty() {
|
|
||||||
return Err(Error::no_witnesses().into());
|
|
||||||
}
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"Running misbehavior detection against {} witnesses...",
|
|
||||||
witnesses.len()
|
|
||||||
);
|
|
||||||
|
|
||||||
let primary_trace = Trace::new(primary_trace)?;
|
|
||||||
|
|
||||||
for witness in witnesses {
|
|
||||||
let divergence = detect_divergence::<Sha256>(
|
|
||||||
Some(primary),
|
|
||||||
witness,
|
|
||||||
primary_trace.clone().into_vec(),
|
|
||||||
max_clock_drift,
|
|
||||||
max_block_lag,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let evidence = match divergence {
|
|
||||||
Ok(Some(divergence)) => divergence.evidence,
|
|
||||||
Ok(None) => {
|
|
||||||
info!(
|
|
||||||
"no divergence found between primary and witness {}",
|
|
||||||
witness.peer_id()
|
|
||||||
);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!(
|
|
||||||
"failed to run attack detector against witness {}: {e}",
|
|
||||||
witness.peer_id()
|
|
||||||
);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Report the evidence to the witness
|
|
||||||
witness
|
|
||||||
.report_evidence(Evidence::from(evidence.against_primary))
|
|
||||||
.await
|
|
||||||
.map_err(|e| eyre!("failed to report evidence to witness: {}", e))?;
|
|
||||||
|
|
||||||
if let Some(against_witness) = evidence.against_witness {
|
|
||||||
// Report the evidence to the primary
|
|
||||||
primary
|
|
||||||
.report_evidence(Evidence::from(against_witness))
|
|
||||||
.await
|
|
||||||
.map_err(|e| eyre!("failed to report evidence to primary: {}", e))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn make_provider(
|
|
||||||
chain_id: &str,
|
|
||||||
rpc_addr: HttpClientUrl,
|
|
||||||
trusted_height: Height,
|
|
||||||
trusted_hash: Hash,
|
|
||||||
options: Options,
|
|
||||||
) -> Result<Provider> {
|
|
||||||
use tendermint_rpc::client::CompatMode;
|
|
||||||
|
|
||||||
let rpc_client = HttpClient::builder(rpc_addr)
|
|
||||||
.compat_mode(CompatMode::V0_34)
|
|
||||||
.build()?;
|
|
||||||
|
|
||||||
let node_id = rpc_client.status().await?.node_info.id;
|
|
||||||
let light_store = Box::new(MemoryStore::new());
|
|
||||||
|
|
||||||
let instance =
|
|
||||||
LightClientBuilder::prod(node_id, rpc_client.clone(), light_store, options, None)
|
|
||||||
.trust_primary_at(trusted_height, trusted_hash)?
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Ok(Provider::new(chain_id.to_string(), instance, rpc_client))
|
|
||||||
}
|
}
|
||||||
|
|
262
utils/tm-prover/src/prover.rs
Normal file
262
utils/tm-prover/src/prover.rs
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
#![deny(
|
||||||
|
warnings,
|
||||||
|
trivial_casts,
|
||||||
|
trivial_numeric_casts,
|
||||||
|
unused_import_braces,
|
||||||
|
unused_qualifications
|
||||||
|
)]
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::{BufWriter, Write},
|
||||||
|
path::PathBuf,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use color_eyre::{
|
||||||
|
eyre::{eyre, Result},
|
||||||
|
Report,
|
||||||
|
};
|
||||||
|
use cw_proof::{
|
||||||
|
error::ProofError,
|
||||||
|
proof::{cw::CwProof, key::CwAbciKey, Proof},
|
||||||
|
};
|
||||||
|
use futures::future::join_all;
|
||||||
|
use tendermint::{crypto::default::Sha256, evidence::Evidence, Hash};
|
||||||
|
use tendermint_light_client::{
|
||||||
|
builder::LightClientBuilder,
|
||||||
|
light_client::Options,
|
||||||
|
store::memory::MemoryStore,
|
||||||
|
types::{Height, LightBlock},
|
||||||
|
};
|
||||||
|
use tendermint_light_client_detector::{detect_divergence, Error, Provider, Trace};
|
||||||
|
use tendermint_rpc::{client::HttpClient, Client, HttpClientUrl};
|
||||||
|
use tracing::{error, info};
|
||||||
|
use tracing_subscriber::{util::SubscriberInitExt, EnvFilter};
|
||||||
|
|
||||||
|
const WASM_STORE_KEY: &str = "/store/wasm/key";
|
||||||
|
|
||||||
|
use crate::config::{Config as TmProverConfig, ProofOutput};
|
||||||
|
|
||||||
|
pub async fn prove(
|
||||||
|
TmProverConfig {
|
||||||
|
chain_id,
|
||||||
|
primary,
|
||||||
|
witnesses,
|
||||||
|
trusted_height,
|
||||||
|
trusted_hash,
|
||||||
|
trust_threshold,
|
||||||
|
trusting_period,
|
||||||
|
max_clock_drift,
|
||||||
|
max_block_lag,
|
||||||
|
trace_file,
|
||||||
|
verbose,
|
||||||
|
contract_address,
|
||||||
|
storage_key,
|
||||||
|
}: TmProverConfig,
|
||||||
|
) -> Result<()> {
|
||||||
|
let env_filter = EnvFilter::builder()
|
||||||
|
.with_default_directive(verbose.to_level_filter().into())
|
||||||
|
.from_env_lossy();
|
||||||
|
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_target(false)
|
||||||
|
.with_env_filter(env_filter)
|
||||||
|
.finish()
|
||||||
|
.init();
|
||||||
|
|
||||||
|
let options = Options {
|
||||||
|
trust_threshold,
|
||||||
|
trusting_period: Duration::from_secs(trusting_period),
|
||||||
|
clock_drift: Duration::from_secs(max_clock_drift),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut provider = make_provider(
|
||||||
|
&chain_id,
|
||||||
|
primary.clone(),
|
||||||
|
trusted_height,
|
||||||
|
trusted_hash,
|
||||||
|
options,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let client = HttpClient::builder(primary.clone()).build()?;
|
||||||
|
|
||||||
|
let trusted_block = provider
|
||||||
|
.latest_trusted()
|
||||||
|
.ok_or_else(|| eyre!("No trusted state found for primary"))?;
|
||||||
|
|
||||||
|
let status = client.status().await?;
|
||||||
|
let latest_height = status.sync_info.latest_block_height;
|
||||||
|
let latest_app_hash = status.sync_info.latest_app_hash;
|
||||||
|
|
||||||
|
// `proof_height` is the height at which we want to query the blockchain's state
|
||||||
|
// This is one less than than the `latest_height` because we want to verify the merkle-proof for
|
||||||
|
// the state against the `app_hash` at `latest_height`.
|
||||||
|
// (because Tendermint commits to the latest `app_hash` in the subsequent block)
|
||||||
|
let proof_height = (latest_height.value() - 1)
|
||||||
|
.try_into()
|
||||||
|
.expect("infallible conversion");
|
||||||
|
|
||||||
|
info!("Verifying to latest height on primary...");
|
||||||
|
|
||||||
|
let primary_block = provider.verify_to_height(latest_height)?;
|
||||||
|
|
||||||
|
info!("Verified to height {} on primary", primary_block.height());
|
||||||
|
let mut primary_trace = provider.get_trace(primary_block.height());
|
||||||
|
let witnesses = join_all(witnesses.0.into_iter().map(|addr: HttpClientUrl| {
|
||||||
|
make_provider(
|
||||||
|
&chain_id,
|
||||||
|
addr,
|
||||||
|
trusted_block.height(),
|
||||||
|
trusted_block.signed_header.header.hash(),
|
||||||
|
options,
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let mut witnesses = witnesses.into_iter().collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
let max_clock_drift = Duration::from_secs(max_clock_drift);
|
||||||
|
let max_block_lag = Duration::from_secs(max_block_lag);
|
||||||
|
|
||||||
|
run_detector(
|
||||||
|
&mut provider,
|
||||||
|
witnesses.as_mut_slice(),
|
||||||
|
primary_trace.clone(),
|
||||||
|
max_clock_drift,
|
||||||
|
max_block_lag,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let path = WASM_STORE_KEY.to_owned();
|
||||||
|
let data = CwAbciKey::new(contract_address, storage_key, None);
|
||||||
|
let result = client
|
||||||
|
.abci_query(Some(path), data, Some(proof_height), true)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let proof: CwProof = result
|
||||||
|
.clone()
|
||||||
|
.try_into()
|
||||||
|
.expect("result should contain proof");
|
||||||
|
proof
|
||||||
|
.verify(latest_app_hash.clone().into())
|
||||||
|
.map_err(|e: ProofError| eyre!(e))?;
|
||||||
|
|
||||||
|
if let Some(trace_file) = trace_file {
|
||||||
|
// replace the last block in the trace (i.e. the (latest - 1) block) with the latest block
|
||||||
|
// we don't actually verify the latest block because it will be verified on the other side
|
||||||
|
let latest_block = provider.fetch_light_block(latest_height)?;
|
||||||
|
let _ = primary_trace.pop();
|
||||||
|
primary_trace.push(latest_block);
|
||||||
|
|
||||||
|
let output = ProofOutput {
|
||||||
|
light_client_proof: primary_trace,
|
||||||
|
merkle_proof: proof.into(),
|
||||||
|
};
|
||||||
|
write_proof_to_file(trace_file, output).await?;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn write_proof_to_file(trace_file: PathBuf, output: ProofOutput) -> Result<()> {
|
||||||
|
info!("Writing proof to output file ({})", trace_file.display());
|
||||||
|
|
||||||
|
let file = File::create(trace_file)?;
|
||||||
|
let mut writer = BufWriter::new(file);
|
||||||
|
serde_json::to_writer(&mut writer, &output)?;
|
||||||
|
writer.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run_detector(
|
||||||
|
primary: &mut Provider,
|
||||||
|
witnesses: &mut [Provider],
|
||||||
|
primary_trace: Vec<LightBlock>,
|
||||||
|
max_clock_drift: Duration,
|
||||||
|
max_block_lag: Duration,
|
||||||
|
) -> Result<(), Report> {
|
||||||
|
if witnesses.is_empty() {
|
||||||
|
return Err(Error::no_witnesses().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Running misbehavior detection against {} witnesses...",
|
||||||
|
witnesses.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
let primary_trace = Trace::new(primary_trace)?;
|
||||||
|
|
||||||
|
for witness in witnesses {
|
||||||
|
let divergence = detect_divergence::<Sha256>(
|
||||||
|
Some(primary),
|
||||||
|
witness,
|
||||||
|
primary_trace.clone().into_vec(),
|
||||||
|
max_clock_drift,
|
||||||
|
max_block_lag,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let evidence = match divergence {
|
||||||
|
Ok(Some(divergence)) => divergence.evidence,
|
||||||
|
Ok(None) => {
|
||||||
|
info!(
|
||||||
|
"no divergence found between primary and witness {}",
|
||||||
|
witness.peer_id()
|
||||||
|
);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!(
|
||||||
|
"failed to run attack detector against witness {}: {e}",
|
||||||
|
witness.peer_id()
|
||||||
|
);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Report the evidence to the witness
|
||||||
|
witness
|
||||||
|
.report_evidence(Evidence::from(evidence.against_primary))
|
||||||
|
.await
|
||||||
|
.map_err(|e| eyre!("failed to report evidence to witness: {}", e))?;
|
||||||
|
|
||||||
|
if let Some(against_witness) = evidence.against_witness {
|
||||||
|
// Report the evidence to the primary
|
||||||
|
primary
|
||||||
|
.report_evidence(Evidence::from(against_witness))
|
||||||
|
.await
|
||||||
|
.map_err(|e| eyre!("failed to report evidence to primary: {}", e))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn make_provider(
|
||||||
|
chain_id: &str,
|
||||||
|
rpc_addr: HttpClientUrl,
|
||||||
|
trusted_height: Height,
|
||||||
|
trusted_hash: Hash,
|
||||||
|
options: Options,
|
||||||
|
) -> Result<Provider> {
|
||||||
|
use tendermint_rpc::client::CompatMode;
|
||||||
|
|
||||||
|
let rpc_client = HttpClient::builder(rpc_addr)
|
||||||
|
.compat_mode(CompatMode::V0_34)
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
let node_id = rpc_client.status().await?.node_info.id;
|
||||||
|
let light_store = Box::new(MemoryStore::new());
|
||||||
|
|
||||||
|
let instance =
|
||||||
|
LightClientBuilder::prod(node_id, rpc_client.clone(), light_store, options, None)
|
||||||
|
.trust_primary_at(trusted_height, trusted_hash)?
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Ok(Provider::new(chain_id.to_string(), instance, rpc_client))
|
||||||
|
}
|
Loading…
Reference in a new issue