feat: cli dev command (#153)
This commit is contained in:
parent
74c3c47e4e
commit
a9ea3fa9ae
53 changed files with 2203 additions and 1371 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -13,4 +13,7 @@ artifacts/
|
|||
.DS_Store
|
||||
.secrets/
|
||||
**/.env.local
|
||||
|
||||
#cli
|
||||
cli/quartz.toml
|
||||
.cache/
|
907
Cargo.lock
generated
907
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -2,7 +2,6 @@
|
|||
resolver = "2"
|
||||
members = [
|
||||
"apps/mtcs/enclave",
|
||||
"apps/transfers/enclave",
|
||||
"cli",
|
||||
"core/light-client-proofs/*",
|
||||
"core/quartz",
|
||||
|
@ -10,7 +9,7 @@ members = [
|
|||
"cosmwasm/packages/*",
|
||||
"utils/*",
|
||||
]
|
||||
exclude = ["apps/mtcs/contracts/cw-tee-mtcs", "apps/transfers/contracts", "apps/mtcs/scripts"]
|
||||
exclude = ["apps/mtcs/contracts/cw-tee-mtcs", "apps/transfers/contracts", "apps/transfers/enclave"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
|
@ -54,7 +53,6 @@ tonic-build = { version = "=0.12.1", default-features = false, features = ["pros
|
|||
tracing = { version = "0.1.39", default-features = false }
|
||||
tracing-subscriber = { version = "0.3.17", default-features = false, features = ["fmt"] }
|
||||
uuid = { version = "1.4.1", default-features = false, features = ["serde"] }
|
||||
walkdir = { version = "2.5.0", default-features = false }
|
||||
x509-cert = { version = "0.2.5", default-features = false }
|
||||
x509-parser = { version = "0.16.0", features = ["default", "verify"] }
|
||||
zeroize = { version = "1.7.0", default-features = false }
|
||||
|
@ -79,7 +77,6 @@ mc-attestation-verifier = { git = "https://github.com/informalsystems/attestatio
|
|||
|
||||
# quartz
|
||||
cw-proof = { path = "core/light-client-proofs/cw-proof", default-features = false }
|
||||
cycles-sync = { path = "utils/cycles-sync", default-features = false }
|
||||
quartz-common = { path = "core/quartz-common"}
|
||||
quartz-cw = { path = "cosmwasm/packages/quartz-cw", default-features = false }
|
||||
quartz-enclave = { path = "core/quartz", default-features = false }
|
||||
|
@ -87,12 +84,12 @@ quartz-proto = { path = "core/quartz-proto", 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 }
|
||||
wasmd-client = { path = "cosmwasm/packages/wasmd-client", 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 }
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
|
|
2
apps/mtcs/.cargo/config.toml
Normal file
2
apps/mtcs/.cargo/config.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[build]
|
||||
target-dir = "target"
|
|
@ -1,7 +1,7 @@
|
|||
# Quartz manifest file
|
||||
|
||||
loader.entrypoint = "file:{{ gramine.libos }}"
|
||||
libos.entrypoint = "{{ quartz_dir }}/target/release/enclave"
|
||||
libos.entrypoint = "{{ quartz_dir }}/target/release/mtcs-enclave"
|
||||
|
||||
loader.log_level = "{{ log_level }}"
|
||||
|
||||
|
@ -43,7 +43,7 @@ sgx.ra_client_linkable = {{ 'true' if ra_client_linkable == '1' else 'false' }}
|
|||
|
||||
sgx.trusted_files = [
|
||||
"file:{{ gramine.libos }}",
|
||||
"file:{{ quartz_dir }}/target/release/enclave",
|
||||
"file:{{ quartz_dir }}/target/release/mtcs-enclave",
|
||||
"file:{{ gramine.runtimedir() }}/",
|
||||
"file:{{ arch_libdir }}/",
|
||||
"file:/usr/{{ arch_libdir }}/",
|
||||
|
|
|
@ -42,7 +42,7 @@ pub struct SubmitObligationsMsg {
|
|||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct SubmitObligationsMsgInner {
|
||||
pub obligations: Vec<RawEncryptedObligation>,
|
||||
pub liquidity_sources: Vec<Addr>,
|
||||
pub liquidity_sources: Vec<LiquiditySource>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
mock_sgx = false
|
||||
tx_sender = "admin"
|
||||
chain_id = "testing"
|
||||
node_url = "127.0.0.1:26657"
|
||||
node_url = "127.0.0.1:25567"
|
||||
enclave_rpc_addr = "http://127.0.0.1"
|
||||
enclave_rpc_port = 11090
|
||||
trusted_hash = "0E01C103E05108B2981C202C5AE41024444E0638A7D8C734916C2797EB1EE447"
|
||||
trusted_height = 8401
|
||||
trusted_hash = ""
|
||||
trusted_height = 0
|
||||
release = true
|
||||
|
|
2
apps/transfers/.cargo/config.toml
Normal file
2
apps/transfers/.cargo/config.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[build]
|
||||
target-dir = "target"
|
|
@ -2,13 +2,12 @@
|
|||
description = "An functioning example of a quartz app"
|
||||
|
||||
ignore = [
|
||||
"target/",
|
||||
"contracts/Cargo.lock",
|
||||
"contracts/target",
|
||||
"contracts/target/*",
|
||||
"contracts/schema",
|
||||
"enclave/Cargo.lock",
|
||||
"enclave/target",
|
||||
"enclave/target/*",
|
||||
"enclave/src/prost/*",
|
||||
"frontend/package-lock.json",
|
||||
"frontend/public/images",
|
||||
"trusted.hash",
|
||||
"trusted.height"
|
||||
]
|
179
apps/transfers/contracts/Cargo.lock
generated
179
apps/transfers/contracts/Cargo.lock
generated
|
@ -149,9 +149,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "asn1-rs"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22ad1373757efa0f70ec53939aabc7152e1591cb485208052993070ac8d2429d"
|
||||
checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048"
|
||||
dependencies = [
|
||||
"asn1-rs-derive",
|
||||
"asn1-rs-impl",
|
||||
|
@ -165,13 +165,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "asn1-rs-derive"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1"
|
||||
checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
@ -183,7 +183,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -243,6 +243,12 @@ version = "3.16.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.7.1"
|
||||
|
@ -251,9 +257,12 @@ checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.6"
|
||||
version = "1.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f"
|
||||
checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -279,15 +288,15 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
|||
|
||||
[[package]]
|
||||
name = "cosmwasm-core"
|
||||
version = "2.1.1"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "367fc87c43759098a476ef90f915aadc66c300480ad9c155b512081fbf327bc1"
|
||||
checksum = "d905990ef3afb5753bb709dc7de88e9e370aa32bcc2f31731d4b533b63e82490"
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-crypto"
|
||||
version = "2.1.1"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7c41f3e371ea457d3b98bb592c38858b46efcf614e0e988ec2ebbdb973954f"
|
||||
checksum = "5b2a7bd9c1dd9a377a4dc0f4ad97d24b03c33798cd5a6d7ceb8869b41c5d2f2d"
|
||||
dependencies = [
|
||||
"ark-bls12-381",
|
||||
"ark-ec",
|
||||
|
@ -308,20 +317,20 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cosmwasm-derive"
|
||||
version = "2.1.1"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c10510e8eb66cf7e109741b1e2c76ad18f30b5a1daa064f5f7115c1f733aaea0"
|
||||
checksum = "029910b409398fdf81955d7301b906caf81f2c42b013ea074fbd89720229c424"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-schema"
|
||||
version = "2.1.1"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f79879b6b7ef6a331b05030ce91ce46a7c4b0baf1ed6b382cce2e9a168109380"
|
||||
checksum = "4bc0d4d85e83438ab9a0fea9348446f7268bc016aacfebce37e998559f151294"
|
||||
dependencies = [
|
||||
"cosmwasm-schema-derive",
|
||||
"schemars",
|
||||
|
@ -332,20 +341,20 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cosmwasm-schema-derive"
|
||||
version = "2.1.1"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82b53e33c0e97170c7ac9cb440f4bc599a07f9cbb9b7e87916cca37b1239d57b"
|
||||
checksum = "edf5c8adac41bb7751c050d7c4c18675be19ee128714454454575e894424eeef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-std"
|
||||
version = "2.1.1"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92011c39570876f340d5f9defa68bf92797b1c44421f1b9ea9b04a31d6defd33"
|
||||
checksum = "51dec99a2e478715c0a4277f0dbeadbb8466500eb7dec873d0924edd086e77f1"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bech32",
|
||||
|
@ -366,9 +375,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.12"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||
checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -444,14 +453,14 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw-multi-test"
|
||||
version = "2.1.0"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0034bfb4c06dfc8b50f0b1a06c3fc0f2312a1bae568a97db65930de071288ba"
|
||||
checksum = "b0ae276e7a06ad1b7e7da78a3d68aba80634cde30ee7fe8259a94e653603fef8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bech32",
|
||||
|
@ -557,7 +566,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -568,7 +577,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
|||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -612,7 +621,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -637,22 +646,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "1.0.0-beta.6"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7abbfc297053be59290e3152f8cbcd52c8642e0728b69ee187d991d4c1af08d"
|
||||
checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05"
|
||||
dependencies = [
|
||||
"derive_more-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more-impl"
|
||||
version = "1.0.0-beta.6"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bba3e9872d7c58ce7ef0fcf1844fcc3e23ef2a58377b50df35dd98e42a5726e"
|
||||
checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
|
@ -676,7 +685,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -886,9 +895,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
|||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.69"
|
||||
version = "0.3.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
|
||||
checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
@ -913,9 +922,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.155"
|
||||
version = "0.2.158"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
|
@ -1116,9 +1125,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
|||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "primeorder"
|
||||
|
@ -1140,9 +1152,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "prost"
|
||||
version = "0.12.6"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29"
|
||||
checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost-derive",
|
||||
|
@ -1150,20 +1162,21 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "prost-derive"
|
||||
version = "0.12.6"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
|
||||
checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.10.5",
|
||||
"itertools 0.13.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quartz-common"
|
||||
version = "0.1.0"
|
||||
source = "git+ssh://git@github.com/informalsystems/cycles-quartz.git#302008c337b58fcad0373922a2787b9341eafd58"
|
||||
dependencies = [
|
||||
"quartz-cw",
|
||||
]
|
||||
|
@ -1171,6 +1184,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "quartz-cw"
|
||||
version = "0.1.0"
|
||||
source = "git+ssh://git@github.com/informalsystems/cycles-quartz.git#302008c337b58fcad0373922a2787b9341eafd58"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
|
@ -1187,6 +1201,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "quartz-tee-ra"
|
||||
version = "0.1.0"
|
||||
source = "git+ssh://git@github.com/informalsystems/cycles-quartz.git#302008c337b58fcad0373922a2787b9341eafd58"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
|
@ -1335,7 +1350,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1360,9 +1375,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.204"
|
||||
version = "1.0.208"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
|
||||
checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
@ -1378,13 +1393,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.204"
|
||||
version = "1.0.208"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||
checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1395,14 +1410,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.122"
|
||||
version = "1.0.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
|
||||
checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
|
@ -1435,7 +1450,7 @@ dependencies = [
|
|||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1449,6 +1464,12 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "2.2.0"
|
||||
|
@ -1506,9 +1527,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.72"
|
||||
version = "2.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
|
||||
checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1523,7 +1544,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1543,7 +1564,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1610,9 +1631,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
|
@ -1634,34 +1655,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.92"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
||||
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.92"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
|
||||
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.92"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
||||
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
@ -1669,22 +1691,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.92"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.92"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
|
@ -1794,6 +1816,7 @@ version = "0.7.35"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
|
@ -1805,7 +1828,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1825,5 +1848,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
|
|
@ -44,7 +44,7 @@ cw20-base = { version = "2.0.0", default-features = false, features = ["library"
|
|||
cw-utils = { version = "2.0.0", default-features = false }
|
||||
|
||||
# quartz
|
||||
quartz-common = { path = "../../../core/quartz-common", default-features = false, features = ["contract"] }
|
||||
quartz-common = { git = "ssh://git@github.com/informalsystems/cycles-quartz.git", features=["contract"]}
|
||||
|
||||
# patch indirect deps
|
||||
getrandom = { version = "0.2.15", default-features = false, features = ["js"] }
|
||||
|
|
|
@ -15,33 +15,32 @@ default = []
|
|||
|
||||
[dependencies]
|
||||
# external
|
||||
anyhow.workspace = true
|
||||
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
|
||||
sha2.workspace = true
|
||||
thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
tonic.workspace = true
|
||||
anyhow = { version = "1.0.86", default-features = false }
|
||||
clap = { version = "4.1.8", default-features = false, features = ["derive", "std"] }
|
||||
color-eyre = { version = "0.6.2", default-features = false }
|
||||
ecies = { version = "0.2.3", default-features = false, features = ["pure"] }
|
||||
hex = { version = "0.4.3", default-features = false }
|
||||
k256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "alloc"] }
|
||||
prost = { version = "=0.13.1", default-features = false }
|
||||
serde = { version = "1.0.203", default-features = false, features = ["derive"] }
|
||||
serde_json = { version = "1.0.94", default-features = false, features = ["alloc"] }
|
||||
sha2 = { version = "0.10.8", default-features = false }
|
||||
thiserror = { version = "1.0.49", default-features = false }
|
||||
tokio = { version = "=1.39.2", default-features = false, features = ["macros", "rt"] }
|
||||
tonic = { version = "=0.12.1", default-features = false, features = ["codegen", "prost", "transport"] }
|
||||
|
||||
# cosmos
|
||||
cosmrs.workspace = true
|
||||
cosmwasm-std.workspace = true
|
||||
cycles-sync.workspace = true
|
||||
tendermint.workspace = true
|
||||
tendermint-light-client.workspace = true
|
||||
transfers-contract.workspace = true
|
||||
cosmrs = { version = "=0.17.0", default-features = false }
|
||||
cosmwasm-std = { version = "2.1.1", default-features = false, features = ["std"] }
|
||||
tendermint = { version = "=0.38.1", default-features = false }
|
||||
tendermint-light-client = { version = "=0.38.1", default-features = false, features = ["rust-crypto"] }
|
||||
transfers-contract = { path = "../contracts", default-features = false }
|
||||
|
||||
# quartz
|
||||
quartz-common = { workspace=true, features=["full"]}
|
||||
quartz-common = { git = "ssh://git@github.com/informalsystems/cycles-quartz.git", features=["full"]}
|
||||
|
||||
[dev-dependencies]
|
||||
cw-multi-test = "2.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build.workspace = true
|
||||
tonic-build = { version = "=0.12.1", default-features = false, features = ["prost", "transport"] }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Quartz-based app manifest file
|
||||
|
||||
loader.entrypoint = "file:{{ gramine.libos }}"
|
||||
libos.entrypoint = "{{ enclave_executable }}"
|
||||
libos.entrypoint = "{{ quartz_dir }}/target/release/quartz-app-transfers-enclave"
|
||||
|
||||
loader.log_level = "{{ log_level }}"
|
||||
|
||||
|
@ -30,7 +30,7 @@ fs.mounts = [
|
|||
{ uri = "file:{{ gramine.runtimedir() }}", path = "/lib" },
|
||||
{ uri = "file:{{ arch_libdir }}", path = "{{ arch_libdir }}" },
|
||||
{ uri = "file:/usr/{{ arch_libdir }}", path = "/usr{{ arch_libdir }}" },
|
||||
{ uri = "file:{{ enclave_dir }}", path = "{{ enclave_dir }}" },
|
||||
{ uri = "file:{{ quartz_dir }}", path = "{{ quartz_dir }}" },
|
||||
]
|
||||
|
||||
# sgx.debug = true
|
||||
|
@ -44,15 +44,15 @@ sgx.ra_client_linkable = {{ 'true' if ra_client_linkable == '1' else 'false' }}
|
|||
|
||||
sgx.trusted_files = [
|
||||
"file:{{ gramine.libos }}",
|
||||
"file:{{ enclave_executable }}",
|
||||
"file:{{ quartz_dir }}/target/release/",
|
||||
"file:{{ gramine.runtimedir() }}/",
|
||||
"file:{{ arch_libdir }}/",
|
||||
"file:/usr/{{ arch_libdir }}/",
|
||||
]
|
||||
|
||||
sgx.allowed_files = [
|
||||
"file:{{ enclave_dir }}/exchange.sk",
|
||||
"file:{{ enclave_dir }}/request.json",
|
||||
"file:{{ quartz_dir }}/exchange.sk",
|
||||
"file:{{ quartz_dir }}/request.json",
|
||||
]
|
||||
|
||||
sys.insecure__allow_eventfd = true
|
||||
|
|
|
@ -84,6 +84,12 @@ impl HasUserData for QueryResponseMessage {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct StatusResponseMessage {
|
||||
address: Addr,
|
||||
encrypted_bal: HexBinary,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
struct AttestedMsg<M> {
|
||||
msg: M,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
mock_sgx = false
|
||||
tx_sender = "admin"
|
||||
chain_id = "testing"
|
||||
node_url = "127.0.0.1:26657"
|
||||
node_url = "127.0.0.1:25567"
|
||||
enclave_rpc_addr = "http://127.0.0.1"
|
||||
enclave_rpc_port = 11090
|
||||
trusted_hash = "0E01C103E05108B2981C202C5AE41024444E0638A7D8C734916C2797EB1EE447"
|
||||
trusted_height = 8401
|
||||
trusted_hash = ""
|
||||
trusted_height = 0
|
||||
release = true
|
||||
|
|
|
@ -27,14 +27,23 @@ prost.workspace = true
|
|||
tokio = { workspace = true, features = ["process"] }
|
||||
tonic.workspace = true
|
||||
once_cell = "1.19.0"
|
||||
reqwest = { version = "0.12.2", default-features = false, features = ["json", "rustls-tls"] }
|
||||
anyhow = "1.0.86"
|
||||
reqwest = { workspace = true, default-features = false, features = ["json", "rustls-tls"] }
|
||||
anyhow.workspace = true
|
||||
base64 = "0.22.1"
|
||||
subtle-encoding = "0.5.1"
|
||||
subtle-encoding.workspace = true
|
||||
futures-util = "0.3.30"
|
||||
target-lexicon = "0.12.16"
|
||||
regex = "1.10.5"
|
||||
watchexec = "4.1.0"
|
||||
watchexec-events = "3.0.0"
|
||||
watchexec-signals = "3.0.0"
|
||||
miette = "7.2.0"
|
||||
ctrlc = { version = "3.4.5", features=["termination"]}
|
||||
xxhash-rust = { version = "0.8.12", features=["xxh3"] }
|
||||
toml = "0.8.19"
|
||||
figment = { version = "0.10.19", features=["env", "toml"] }
|
||||
clearscreen = "3.0.0"
|
||||
cargo_metadata = "0.18.1"
|
||||
|
||||
# cosmos
|
||||
cosmrs.workspace = true
|
||||
|
@ -43,9 +52,9 @@ tendermint.workspace = true
|
|||
tendermint-light-client.workspace = true
|
||||
tendermint-rpc = { workspace = true, features=["websocket-client", "http-client"]}
|
||||
|
||||
cycles-sync = { workspace = true}
|
||||
tm-prover = { workspace = true}
|
||||
quartz-common = { workspace = true, features=["contract"]}
|
||||
quartz-tee-ra = { workspace = true}
|
||||
mtcs-enclave = { workspace = true, optional = false}
|
||||
figment = { version = "0.10.19", features=["env", "toml"] }
|
||||
wasmd-client.workspace = true
|
||||
tempfile.workspace = true
|
||||
|
|
151
cli/src/cache.rs
Normal file
151
cli/src/cache.rs
Normal file
|
@ -0,0 +1,151 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::{
|
||||
fs::File,
|
||||
io::{AsyncReadExt, BufReader},
|
||||
};
|
||||
use tracing::debug;
|
||||
use xxhash_rust::xxh3::Xxh3;
|
||||
|
||||
use crate::{config::Config, error::Error};
|
||||
|
||||
const BUFFER_SIZE: usize = 16384; // 16 KB buffer
|
||||
type Hash = u64;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
struct DeployedContract {
|
||||
code_id: u64,
|
||||
contract_hash: Hash,
|
||||
}
|
||||
|
||||
// Porcelain
|
||||
|
||||
impl Config {
|
||||
pub async fn contract_has_changed(&self, file: &Path) -> Result<bool, Error> {
|
||||
let cur_hash: Hash = Self::gen_hash(file).await?;
|
||||
debug!("current file hash: {}", cur_hash);
|
||||
|
||||
let cached_file_path = Self::to_cache_path(self, file)?;
|
||||
|
||||
if !cached_file_path.exists() {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let cached_contract = Self::read_from_cache(cached_file_path.as_path()).await?;
|
||||
debug!("cached file hash: {}", cached_contract.contract_hash);
|
||||
|
||||
Ok(cur_hash != cached_contract.contract_hash)
|
||||
}
|
||||
|
||||
/// Return a hash of the given file's contents
|
||||
pub async fn gen_hash(file: &Path) -> Result<Hash, Error> {
|
||||
let file = File::open(file)
|
||||
.await
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
let mut reader = BufReader::new(file);
|
||||
|
||||
let mut hasher = Xxh3::new();
|
||||
|
||||
let mut buffer = [0; BUFFER_SIZE];
|
||||
loop {
|
||||
let bytes_read = reader
|
||||
.read(&mut buffer)
|
||||
.await
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
if bytes_read == 0 {
|
||||
break;
|
||||
}
|
||||
hasher.update(&buffer[..bytes_read]);
|
||||
}
|
||||
|
||||
// Finalize the hash
|
||||
let hash = hasher.digest();
|
||||
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
pub async fn save_codeid_to_cache(&self, file: &Path, code_id: u64) -> Result<(), Error> {
|
||||
let contract_hash = Self::gen_hash(file).await?;
|
||||
let dest = Self::to_cache_path(self, file)?;
|
||||
let deployed_contract = DeployedContract {
|
||||
code_id,
|
||||
contract_hash,
|
||||
};
|
||||
|
||||
Self::write_to_cache(dest.as_path(), &deployed_contract).await
|
||||
}
|
||||
|
||||
pub async fn get_cached_codeid(&self, file: &Path) -> Result<u64, Error> {
|
||||
let cache_path = Self::to_cache_path(self, file)?;
|
||||
let code_id = Self::read_from_cache(cache_path.as_path()).await?.code_id;
|
||||
|
||||
Ok(code_id)
|
||||
}
|
||||
|
||||
// Plumbing
|
||||
|
||||
fn to_cache_path(&self, file: &Path) -> Result<PathBuf, Error> {
|
||||
// Get cache filepath (".quartz/cache/example.wasm.json") from "example.wasm" filepath
|
||||
let mut filename = file
|
||||
.file_name()
|
||||
.ok_or(Error::PathNotFile(file.display().to_string()))?
|
||||
.to_os_string();
|
||||
filename.push(".json");
|
||||
|
||||
let cached_file_path = Self::cache_dir(self)?.join::<PathBuf>(filename.into());
|
||||
|
||||
Ok(cached_file_path)
|
||||
}
|
||||
|
||||
/// Retreive hash from cache file
|
||||
async fn read_from_cache(cache_file: &Path) -> Result<DeployedContract, Error> {
|
||||
let content = tokio::fs::read_to_string(cache_file)
|
||||
.await
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
serde_json::from_str(&content).map_err(|e| Error::GenericErr(e.to_string()))
|
||||
}
|
||||
|
||||
/// Write a given file's contents hash to a file in cache directory
|
||||
async fn write_to_cache(cache_file: &Path, data: &DeployedContract) -> Result<(), Error> {
|
||||
let content = serde_json::to_string(data).map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
tokio::fs::write(cache_file, content)
|
||||
.await
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))
|
||||
}
|
||||
|
||||
pub fn cache_dir(&self) -> Result<PathBuf, Error> {
|
||||
Ok(self.app_dir.join(".cache/"))
|
||||
}
|
||||
|
||||
pub fn build_log_dir(&self) -> Result<PathBuf, Error> {
|
||||
Ok(self.app_dir.join(".cache/log/"))
|
||||
}
|
||||
|
||||
/// Creates the build log if it isn't created already, returns relative path from app_dir to log directory
|
||||
pub async fn create_build_log(&self) -> Result<PathBuf, Error> {
|
||||
let log_dir = Self::build_log_dir(self)?;
|
||||
if !log_dir.exists() {
|
||||
tokio::fs::create_dir_all(&log_dir)
|
||||
.await
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
}
|
||||
|
||||
Ok(log_dir)
|
||||
}
|
||||
|
||||
pub async fn log_build(&self, is_enclave: bool) -> Result<(), Error> {
|
||||
let log_dir = Self::create_build_log(self).await?;
|
||||
|
||||
let filename = match is_enclave {
|
||||
true => "enclave",
|
||||
false => "contract",
|
||||
};
|
||||
|
||||
tokio::fs::write(log_dir.join(filename), "test")
|
||||
.await
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -35,8 +35,8 @@ pub struct Cli {
|
|||
/// Enable mock SGX mode for testing purposes.
|
||||
/// This flag disables the use of an Intel SGX processor and allows the system to run without remote attestations.
|
||||
#[arg(long)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub mock_sgx: Option<bool>,
|
||||
#[serde(skip_serializing_if = "is_false")]
|
||||
pub mock_sgx: bool,
|
||||
|
||||
/// Path to Quartz app directory
|
||||
/// Defaults to current working dir
|
||||
|
@ -50,6 +50,10 @@ pub struct Cli {
|
|||
pub command: Command,
|
||||
}
|
||||
|
||||
fn is_false(b: &bool) -> bool {
|
||||
!(*b)
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand, Serialize, Clone)]
|
||||
pub enum Command {
|
||||
/// Create an empty Quartz app from a template
|
||||
|
@ -69,6 +73,8 @@ pub enum Command {
|
|||
#[command(subcommand)]
|
||||
enclave_command: EnclaveCommand,
|
||||
},
|
||||
/// Build, deploy, perform handshake, and run quartz app while listening for changes
|
||||
Dev(DevArgs),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Subcommand, Serialize)]
|
||||
|
@ -98,6 +104,10 @@ pub struct HandshakeArgs {
|
|||
#[arg(short, long, value_parser = wasmaddr_to_id)]
|
||||
pub contract: AccountId,
|
||||
|
||||
/// Fetch latest trusted hash and height from the chain instead of existing configuration
|
||||
#[arg(long)]
|
||||
pub use_latest_trusted: bool,
|
||||
|
||||
/// Name or address of private key with which to sign
|
||||
#[arg(long)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -126,8 +136,9 @@ pub struct HandshakeArgs {
|
|||
|
||||
#[derive(Debug, Parser, Clone, Serialize, Deserialize)]
|
||||
pub struct ContractBuildArgs {
|
||||
/// Path to Cargo manifest file for CosmWasm contract package
|
||||
#[arg(long)]
|
||||
pub manifest_path: PathBuf,
|
||||
pub contract_manifest: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser, Clone, Serialize, Deserialize)]
|
||||
|
@ -155,16 +166,17 @@ pub struct ContractDeployArgs {
|
|||
#[arg(long, default_value = "Quartz App Contract")]
|
||||
pub label: String,
|
||||
|
||||
/// Path to contract wasm binary for deployment
|
||||
/// Path to Cargo manifest file for CosmWasm contract package
|
||||
#[arg(long)]
|
||||
pub wasm_bin_path: PathBuf,
|
||||
pub contract_manifest: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser, Clone, Serialize, Deserialize)]
|
||||
pub struct EnclaveBuildArgs {
|
||||
/// Path to Cargo.toml file of the Quartz app's enclave package, defaults to './enclave/Cargo.toml' if unspecified
|
||||
#[arg(long, default_value = "./enclave/Cargo.toml")]
|
||||
pub manifest_path: PathBuf,
|
||||
/// Whether to target release or dev
|
||||
#[arg(long)]
|
||||
#[serde(skip_serializing_if = "is_false")]
|
||||
pub release: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser, Clone, Serialize, Deserialize)]
|
||||
|
@ -177,6 +189,28 @@ pub struct EnclaveStartArgs {
|
|||
/// Fetch latest trusted hash and height from the chain instead of existing configuration
|
||||
#[arg(long)]
|
||||
pub use_latest_trusted: bool,
|
||||
|
||||
/// Whether to target release or dev
|
||||
#[arg(long)]
|
||||
#[serde(skip_serializing_if = "is_false")]
|
||||
pub release: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser, Clone, Serialize, Deserialize)]
|
||||
pub struct DevArgs {
|
||||
/// Automatically deploy and instantiate new cosmwasm contract instance upon changes to source
|
||||
#[arg(long)]
|
||||
pub watch: bool,
|
||||
|
||||
/// Fetch latest trusted hash and height from the chain instead of existing configuration
|
||||
#[arg(long)]
|
||||
pub use_latest_trusted: bool,
|
||||
|
||||
#[command(flatten)]
|
||||
pub contract_deploy: ContractDeployArgs,
|
||||
|
||||
#[command(flatten)]
|
||||
pub enclave_build: EnclaveBuildArgs,
|
||||
}
|
||||
|
||||
pub trait ToFigment {
|
||||
|
@ -196,6 +230,9 @@ impl ToFigment for Command {
|
|||
EnclaveCommand::Build(args) => Figment::from(Serialized::defaults(args)),
|
||||
EnclaveCommand::Start(args) => Figment::from(Serialized::defaults(args)),
|
||||
},
|
||||
Command::Dev(args) => Figment::from(Serialized::defaults(args))
|
||||
.merge(Serialized::defaults(&args.contract_deploy))
|
||||
.merge(Serialized::defaults(&args.enclave_build)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ pub struct Config {
|
|||
|
||||
/// Path to Quartz app directory
|
||||
/// Defaults to current working dir
|
||||
#[serde(default = "default_app_dir")]
|
||||
#[serde(default = "default_app_dir", skip_serializing)]
|
||||
pub app_dir: PathBuf,
|
||||
|
||||
/// Trusted height for light client proofs
|
||||
|
@ -42,6 +42,10 @@ pub struct Config {
|
|||
/// Trusted hash for block at trusted_height for light client proofs
|
||||
#[serde(default)]
|
||||
pub trusted_hash: String,
|
||||
|
||||
/// Whether to build for release or debug
|
||||
#[serde(default)]
|
||||
pub release: bool,
|
||||
}
|
||||
|
||||
fn default_rpc_addr() -> String {
|
||||
|
@ -80,6 +84,7 @@ impl Default for Config {
|
|||
app_dir: default_app_dir(),
|
||||
trusted_height: u64::default(),
|
||||
trusted_hash: String::default(),
|
||||
release: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,3 +94,9 @@ impl AsRef<Config> for Config {
|
|||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn enclave_rpc(&self) -> String {
|
||||
format!("{}:{}", self.enclave_rpc_addr, self.enclave_rpc_port)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ pub enum Error {
|
|||
TomlError(String),
|
||||
/// Tendermint error: {0}
|
||||
TendermintError(String),
|
||||
/// Clearscreen error: {0}
|
||||
ClearscreenError(String),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
|
@ -40,3 +42,9 @@ impl From<tendermint::Error> for Error {
|
|||
Error::TendermintError(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<clearscreen::Error> for Error {
|
||||
fn from(err: clearscreen::Error) -> Self {
|
||||
Error::ClearscreenError(err.to_string())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ pub mod utils;
|
|||
// commands
|
||||
pub mod contract_build;
|
||||
pub mod contract_deploy;
|
||||
pub mod dev;
|
||||
pub mod enclave_build;
|
||||
pub mod enclave_start;
|
||||
pub mod handshake;
|
||||
|
@ -38,6 +39,7 @@ impl Handler for Request {
|
|||
Request::ContractDeploy(request) => request.handle(config).await,
|
||||
Request::EnclaveBuild(request) => request.handle(config).await,
|
||||
Request::EnclaveStart(request) => request.handle(config).await,
|
||||
Request::Dev(request) => request.handle(config).await,
|
||||
}
|
||||
.map(Into::into)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::process::Command;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use tracing::{debug, trace};
|
||||
use color_eyre::owo_colors::OwoColorize;
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::{
|
||||
config::Config,
|
||||
|
@ -21,11 +22,22 @@ impl Handler for ContractBuildRequest {
|
|||
config: C,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let config = config.as_ref();
|
||||
info!("{}", "\nPeforming Contract Build".blue().bold());
|
||||
|
||||
let mut cargo = Command::new("cargo");
|
||||
let command = cargo
|
||||
.arg("wasm")
|
||||
.args(["--manifest-path", &self.manifest_path.display().to_string()])
|
||||
.arg("build")
|
||||
.arg("--release")
|
||||
.args(["--target", "wasm32-unknown-unknown"])
|
||||
.arg("--lib")
|
||||
.args([
|
||||
"--target-dir",
|
||||
&config.app_dir.join("target").display().to_string(),
|
||||
])
|
||||
.args([
|
||||
"--manifest-path",
|
||||
&self.contract_manifest.display().to_string(),
|
||||
])
|
||||
.env("RUSTFLAGS", "-C link-arg=-s");
|
||||
|
||||
if config.mock_sgx {
|
||||
|
@ -33,7 +45,7 @@ impl Handler for ContractBuildRequest {
|
|||
command.arg("--features=mock-sgx");
|
||||
}
|
||||
|
||||
trace!("🚧 Building contract binary ...");
|
||||
info!("{}", "🚧 Building contract binary ...".green().bold());
|
||||
let status = command
|
||||
.status()
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
|
@ -45,6 +57,8 @@ impl Handler for ContractBuildRequest {
|
|||
)));
|
||||
}
|
||||
|
||||
config.log_build(false).await?;
|
||||
|
||||
Ok(ContractBuildResponse.into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::env::current_dir;
|
||||
use std::path::Path;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use cycles_sync::wasmd_client::{CliWasmdClient, WasmdClient};
|
||||
use cargo_metadata::MetadataCommand;
|
||||
use color_eyre::owo_colors::OwoColorize;
|
||||
use quartz_common::contract::{
|
||||
msg::execute::attested::{RawEpidAttestation, RawMockAttestation},
|
||||
prelude::QuartzInstantiateMsg,
|
||||
|
@ -10,16 +11,20 @@ use reqwest::Url;
|
|||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use serde_json::json;
|
||||
use tendermint_rpc::HttpClient;
|
||||
use tracing::{debug, info, trace};
|
||||
use tracing::{debug, info};
|
||||
use wasmd_client::{CliWasmdClient, WasmdClient};
|
||||
|
||||
use super::utils::{
|
||||
helpers::{block_tx_commit, run_relay},
|
||||
helpers::block_tx_commit,
|
||||
types::{Log, WasmdTxResponse},
|
||||
};
|
||||
use crate::{
|
||||
config::Config,
|
||||
error::Error,
|
||||
handler::{utils::types::RelayMessage, Handler},
|
||||
handler::{
|
||||
utils::{helpers::run_relay_rust, types::RelayMessage},
|
||||
Handler,
|
||||
},
|
||||
request::contract_deploy::ContractDeployRequest,
|
||||
response::{contract_deploy::ContractDeployResponse, Response},
|
||||
};
|
||||
|
@ -34,15 +39,32 @@ impl Handler for ContractDeployRequest {
|
|||
config: C,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let config = config.as_ref();
|
||||
info!("{}", "\nPeforming Contract Deploy".blue().bold());
|
||||
|
||||
trace!("initializing directory structure...");
|
||||
// Get contract package name in snake_case
|
||||
let package_name = MetadataCommand::new()
|
||||
.manifest_path(&self.contract_manifest)
|
||||
.exec()
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?
|
||||
.root_package()
|
||||
.ok_or("No root package found in the metadata")
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?
|
||||
.name
|
||||
.clone()
|
||||
.replace('-', "_");
|
||||
|
||||
let wasm_bin_path = config
|
||||
.app_dir
|
||||
.join("target/wasm32-unknown-unknown/release")
|
||||
.join(package_name)
|
||||
.with_extension("wasm");
|
||||
|
||||
let (code_id, contract_addr) = if config.mock_sgx {
|
||||
deploy::<RawMockAttestation>(self, config)
|
||||
deploy::<RawMockAttestation>(wasm_bin_path.as_path(), self, config)
|
||||
.await
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?
|
||||
} else {
|
||||
deploy::<RawEpidAttestation>(self, config)
|
||||
deploy::<RawEpidAttestation>(wasm_bin_path.as_path(), self, config)
|
||||
.await
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?
|
||||
};
|
||||
|
@ -56,40 +78,41 @@ impl Handler for ContractDeployRequest {
|
|||
}
|
||||
|
||||
async fn deploy<DA: Serialize + DeserializeOwned>(
|
||||
wasm_bin_path: &Path,
|
||||
args: ContractDeployRequest,
|
||||
config: &Config,
|
||||
) -> Result<(u64, String), anyhow::Error> {
|
||||
// TODO: Replace with call to Rust package
|
||||
let relay_path = current_dir()?.join("../");
|
||||
|
||||
let httpurl = Url::parse(&format!("http://{}", config.node_url))?;
|
||||
let tmrpc_client = HttpClient::new(httpurl.as_str())?;
|
||||
let wasmd_client = CliWasmdClient::new(Url::parse(httpurl.as_str())?);
|
||||
|
||||
info!("\n🚀 Deploying {} Contract\n", args.label);
|
||||
let contract_path = args.wasm_bin_path;
|
||||
// .join("contracts/cw-tee-mtcs/target/wasm32-unknown-unknown/release/cw_tee_mtcs.wasm");
|
||||
|
||||
// TODO: uncertain about the path -> string conversion
|
||||
info!("🚀 Deploying {} Contract", args.label);
|
||||
let code_id = if config.contract_has_changed(wasm_bin_path).await? {
|
||||
let deploy_output: WasmdTxResponse = serde_json::from_str(&wasmd_client.deploy(
|
||||
&config.chain_id,
|
||||
&config.tx_sender,
|
||||
contract_path.display().to_string(),
|
||||
wasm_bin_path.display().to_string(),
|
||||
)?)?;
|
||||
let res = block_tx_commit(&tmrpc_client, deploy_output.txhash).await?;
|
||||
|
||||
let log: Vec<Log> = serde_json::from_str(&res.tx_result.log)?;
|
||||
let code_id: u64 = log[0].events[1].attributes[1].value.parse()?;
|
||||
config.save_codeid_to_cache(wasm_bin_path, code_id).await?;
|
||||
|
||||
info!("\n🚀 Communicating with Relay to Instantiate...\n");
|
||||
let raw_init_msg = run_relay::<QuartzInstantiateMsg<DA>>(
|
||||
relay_path.as_path(),
|
||||
code_id
|
||||
} else {
|
||||
config.get_cached_codeid(wasm_bin_path).await?
|
||||
};
|
||||
|
||||
info!("🚀 Communicating with Relay to Instantiate...");
|
||||
let raw_init_msg = run_relay_rust::<QuartzInstantiateMsg<DA>>(
|
||||
config.enclave_rpc(),
|
||||
config.mock_sgx,
|
||||
RelayMessage::Instantiate,
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!("\n🚀 Instantiating {} Contract\n", args.label);
|
||||
info!("🚀 Instantiating {}", args.label);
|
||||
let mut init_msg = args.init_msg;
|
||||
init_msg["quartz"] = json!(raw_init_msg);
|
||||
|
||||
|
@ -105,9 +128,9 @@ async fn deploy<DA: Serialize + DeserializeOwned>(
|
|||
let log: Vec<Log> = serde_json::from_str(&res.tx_result.log)?;
|
||||
let contract_addr: &String = &log[0].events[1].attributes[0].value;
|
||||
|
||||
info!("\n🚀 Successfully deployed and instantiated contract!");
|
||||
info!("\n🆔 Code ID: {}", code_id);
|
||||
info!("\n📌 Contract Address: {}", contract_addr);
|
||||
info!("🚀 Successfully deployed and instantiated contract!");
|
||||
info!("🆔 Code ID: {}", code_id);
|
||||
info!("📌 Contract Address: {}", contract_addr);
|
||||
|
||||
debug!("{contract_addr}");
|
||||
|
||||
|
|
348
cli/src/handler/dev.rs
Normal file
348
cli/src/handler/dev.rs
Normal file
|
@ -0,0 +1,348 @@
|
|||
use std::{path::PathBuf, process::exit, time::Duration};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use color_eyre::owo_colors::OwoColorize;
|
||||
// todo get rid of this?
|
||||
use miette::{IntoDiagnostic, Result};
|
||||
use quartz_common::proto::core_client::CoreClient;
|
||||
use tokio::{
|
||||
sync::{mpsc, watch},
|
||||
time::sleep,
|
||||
};
|
||||
use tracing::{debug, info};
|
||||
use watchexec::Watchexec;
|
||||
use watchexec_signals::Signal;
|
||||
|
||||
use crate::{
|
||||
error::Error,
|
||||
handler::{utils::helpers::wasmaddr_to_id, Handler},
|
||||
request::{
|
||||
contract_build::ContractBuildRequest, contract_deploy::ContractDeployRequest,
|
||||
dev::DevRequest, enclave_build::EnclaveBuildRequest, enclave_start::EnclaveStartRequest,
|
||||
handshake::HandshakeRequest,
|
||||
},
|
||||
response::{dev::DevResponse, Response},
|
||||
Config, BANNER,
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
impl Handler for DevRequest {
|
||||
type Error = Error;
|
||||
type Response = Response;
|
||||
|
||||
async fn handle<C: AsRef<Config> + Send>(
|
||||
self,
|
||||
config: C,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let config = config.as_ref();
|
||||
info!("\nPeforming Dev");
|
||||
|
||||
let (tx, rx) = mpsc::channel::<DevRebuild>(32);
|
||||
let _res = tx.send(DevRebuild::Init).await;
|
||||
|
||||
if self.watch {
|
||||
tokio::spawn(watcher(tx, config.build_log_dir()?));
|
||||
}
|
||||
|
||||
dev_driver(rx, &self, config.clone()).await?;
|
||||
|
||||
Ok(DevResponse.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum DevRebuild {
|
||||
Init,
|
||||
Enclave,
|
||||
Contract,
|
||||
}
|
||||
|
||||
async fn dev_driver(
|
||||
mut rx: mpsc::Receiver<DevRebuild>,
|
||||
args: &DevRequest,
|
||||
config: Config,
|
||||
) -> Result<(), Error> {
|
||||
// State
|
||||
let mut shutdown_tx: Option<watch::Sender<()>> = None;
|
||||
let mut first_enclave_message = true;
|
||||
let mut first_contract_message = true;
|
||||
let mut contract = String::from("");
|
||||
|
||||
// Shutdown enclave upon interruption
|
||||
let shutdown_tx_cpy = shutdown_tx.clone();
|
||||
ctrlc::set_handler(move || {
|
||||
if let Some(tx) = &shutdown_tx_cpy {
|
||||
let _res = tx.send(());
|
||||
}
|
||||
|
||||
exit(130)
|
||||
})
|
||||
.expect("Error setting Ctrl-C handler");
|
||||
|
||||
// Drive
|
||||
while let Some(dev) = rx.recv().await {
|
||||
match dev {
|
||||
DevRebuild::Init => {
|
||||
clearscreen::clear()?;
|
||||
println!("{}", BANNER.yellow().bold());
|
||||
info!("{}", "Launching quartz app...".green().bold());
|
||||
|
||||
// Build enclave
|
||||
let enclave_build = EnclaveBuildRequest {
|
||||
release: args.release,
|
||||
};
|
||||
enclave_build.handle(&config).await?;
|
||||
|
||||
// Build contract
|
||||
let contract_build = ContractBuildRequest {
|
||||
contract_manifest: args.contract_manifest.clone(),
|
||||
};
|
||||
contract_build.handle(&config).await?;
|
||||
|
||||
// Start enclave in background
|
||||
let new_shutdown_tx = spawn_enclave_start(args, &config).await?;
|
||||
|
||||
// Deploy new contract and perform handshake
|
||||
let res = deploy_and_handshake(None, args, &config).await;
|
||||
|
||||
// Save resulting contract address or shutdown and return error
|
||||
match res {
|
||||
Ok(res_contract) => {
|
||||
// Set state
|
||||
contract = res_contract;
|
||||
shutdown_tx = Some(new_shutdown_tx);
|
||||
|
||||
info!("{}", "Enclave is listening for requests...".green().bold());
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error launching quartz app");
|
||||
|
||||
new_shutdown_tx
|
||||
.send(())
|
||||
.expect("Could not send signal on channel");
|
||||
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
DevRebuild::Enclave => {
|
||||
if first_enclave_message {
|
||||
first_enclave_message = false;
|
||||
|
||||
continue;
|
||||
}
|
||||
clearscreen::clear()?;
|
||||
println!("{}", BANNER.yellow().bold());
|
||||
info!("{}", "Rebuilding Enclave...".green().bold());
|
||||
|
||||
if let Some(shutdown_tx) = shutdown_tx.clone() {
|
||||
let _res = shutdown_tx.send(());
|
||||
}
|
||||
|
||||
info!("Waiting 1 second for the enclave to shut down");
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
|
||||
let new_shutdown_tx = spawn_enclave_start(args, &config).await?;
|
||||
|
||||
// todo: should not unconditionally deploy here
|
||||
let res = deploy_and_handshake(Some(&contract), args, &config).await;
|
||||
|
||||
match res {
|
||||
Ok(res_contract) => {
|
||||
// Set state
|
||||
contract = res_contract;
|
||||
shutdown_tx = Some(new_shutdown_tx);
|
||||
|
||||
info!("{}", "Enclave is listening for requests...".green().bold());
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error restarting enclave and handshake");
|
||||
|
||||
new_shutdown_tx
|
||||
.send(())
|
||||
.expect("Could not send signal on channel");
|
||||
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
DevRebuild::Contract => {
|
||||
if first_contract_message {
|
||||
first_contract_message = false;
|
||||
continue;
|
||||
}
|
||||
clearscreen::clear()?;
|
||||
println!("{}", BANNER.yellow().bold());
|
||||
info!("{}", "Rebuilding Contract...".green().bold());
|
||||
|
||||
if let Some(shutdown_tx) = shutdown_tx.clone() {
|
||||
let res = deploy_and_handshake(None, args, &config).await;
|
||||
|
||||
match res {
|
||||
Ok(res_contract) => contract = res_contract,
|
||||
Err(e) => {
|
||||
eprintln!("Error deploying contract and handshake:");
|
||||
|
||||
shutdown_tx
|
||||
.send(())
|
||||
.expect("Could not send signal on channel");
|
||||
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(Error::GenericErr(
|
||||
"Attempting to redeploy contract, but enclave isn't running".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
info!("{}", "Enclave is listening for requests...".green().bold());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Spawns enclve start in a separate task which runs in the background
|
||||
async fn spawn_enclave_start(
|
||||
args: &DevRequest,
|
||||
config: &Config,
|
||||
) -> Result<watch::Sender<()>, Error> {
|
||||
// In separate process, launch the enclave
|
||||
let (shutdown_tx, shutdown_rx) = watch::channel(());
|
||||
let enclave_start = EnclaveStartRequest {
|
||||
shutdown_rx: Some(shutdown_rx),
|
||||
use_latest_trusted: args.use_latest_trusted,
|
||||
};
|
||||
|
||||
let config_cpy = config.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let res = enclave_start.handle(config_cpy).await?;
|
||||
|
||||
Ok::<Response, Error>(res)
|
||||
});
|
||||
|
||||
Ok(shutdown_tx)
|
||||
}
|
||||
|
||||
// TODO: do not shutdown if cli calls fail, just print
|
||||
async fn deploy_and_handshake(
|
||||
contract: Option<&str>,
|
||||
args: &DevRequest,
|
||||
config: &Config,
|
||||
) -> Result<String, Error> {
|
||||
info!("Waiting for enclave start to deploy contract and handshake");
|
||||
|
||||
// Wait at most 30 seconds to connect to enclave
|
||||
let mut i = 30;
|
||||
while CoreClient::connect(format!(
|
||||
"{}:{}",
|
||||
config.enclave_rpc_addr, config.enclave_rpc_port
|
||||
))
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
i -= 1;
|
||||
|
||||
if i == 0 {
|
||||
return Err(Error::GenericErr(
|
||||
"Could not connect to enclave".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
// Calls which interact with enclave
|
||||
info!("Successfully pinged enclave, enclave is running");
|
||||
|
||||
// Deploy contract IF existing contract wasn't pass into the function
|
||||
let contract = if let Some(contract) = contract {
|
||||
info!("Contract already deployed, reusing");
|
||||
contract.to_string()
|
||||
} else {
|
||||
info!("Deploying contract");
|
||||
// Deploy Contract request
|
||||
let contract_deploy = ContractDeployRequest {
|
||||
init_msg: args.init_msg.clone(),
|
||||
label: args.label.clone(),
|
||||
contract_manifest: args.contract_manifest.clone(),
|
||||
};
|
||||
// Call handler
|
||||
let cd_res = contract_deploy.handle(config).await;
|
||||
|
||||
// Return contract address or shutdown enclave & error
|
||||
match cd_res {
|
||||
Ok(Response::ContractDeploy(res)) => res.contract_addr,
|
||||
Err(e) => return Err(e),
|
||||
_ => unreachable!("Unexpected response variant"),
|
||||
}
|
||||
};
|
||||
|
||||
// Run handshake
|
||||
info!("Running handshake on contract `{}`", contract);
|
||||
let handshake = HandshakeRequest {
|
||||
contract: wasmaddr_to_id(&contract).map_err(|_| Error::GenericErr(String::default()))?,
|
||||
use_latest_trusted: args.use_latest_trusted,
|
||||
};
|
||||
|
||||
let h_res = handshake.handle(config).await;
|
||||
|
||||
match h_res {
|
||||
Ok(Response::Handshake(res)) => {
|
||||
info!("Handshake complete: {}", res.pub_key);
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
_ => unreachable!("Unexpected response variant"),
|
||||
};
|
||||
|
||||
Ok(contract)
|
||||
}
|
||||
|
||||
async fn watcher(tx: mpsc::Sender<DevRebuild>, log_dir: PathBuf) -> Result<()> {
|
||||
let wx = Watchexec::new_async(move |mut action| {
|
||||
let tx = tx.clone();
|
||||
|
||||
Box::new(async move {
|
||||
if action.signals().any(|sig| sig == Signal::Interrupt) {
|
||||
eprintln!("[Quitting...]");
|
||||
action.quit();
|
||||
return action;
|
||||
}
|
||||
|
||||
// Look for updates to filepaths
|
||||
if let Some((path, _)) = action.paths().next() {
|
||||
debug!("event triggered on path:\n {:?}", path);
|
||||
if path
|
||||
.file_name()
|
||||
.expect("events can't trigger on nonexistent files")
|
||||
.eq("enclave")
|
||||
{
|
||||
let _res = tx.send(DevRebuild::Enclave).await;
|
||||
} else if path
|
||||
.file_name()
|
||||
.expect("events can't trigger on nonexistent files")
|
||||
.eq("contract")
|
||||
{
|
||||
let _res = tx.send(DevRebuild::Contract).await;
|
||||
}
|
||||
}
|
||||
|
||||
action
|
||||
})
|
||||
})?;
|
||||
|
||||
// Start the engine
|
||||
let main = wx.main();
|
||||
|
||||
// Watch all files in quartz app directory
|
||||
// TODO: should create_log_dir be called instead? Just enforce building log in all cases?
|
||||
wx.config.pathset([log_dir]);
|
||||
|
||||
// Keep running until Watchexec quits
|
||||
let _ = main.await.into_diagnostic()?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use std::process::Command;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use color_eyre::owo_colors::OwoColorize;
|
||||
use tokio::process::Command;
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::{
|
||||
|
@ -21,20 +21,31 @@ impl Handler for EnclaveBuildRequest {
|
|||
config: C,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let config = config.as_ref();
|
||||
info!("{}", "\nPeforming Enclave Build".blue().bold());
|
||||
|
||||
let enclave_dir = config.app_dir.join("enclave");
|
||||
|
||||
let mut cargo = Command::new("cargo");
|
||||
let command = cargo
|
||||
.args(["build", "--release"])
|
||||
.args(["--manifest-path", &self.manifest_path.display().to_string()]);
|
||||
.arg("build")
|
||||
.args(["--target-dir", &config.app_dir.join("target").display().to_string()]) // TODO: Where should this be set to?
|
||||
.args(["--manifest-path", &enclave_dir.join("Cargo.toml").display().to_string(),
|
||||
]);
|
||||
|
||||
if config.mock_sgx {
|
||||
debug!("Building with mock-sgx enabled");
|
||||
command.arg("--features=mock-sgx");
|
||||
}
|
||||
|
||||
info!("🚧 Building enclave ...");
|
||||
if self.release {
|
||||
debug!("Targetting release");
|
||||
command.arg("--release");
|
||||
}
|
||||
|
||||
info!("{}", "🚧 Running build command ...".green().bold());
|
||||
let status = command
|
||||
.status()
|
||||
.await
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
|
||||
if !status.success() {
|
||||
|
@ -44,6 +55,8 @@ impl Handler for EnclaveBuildRequest {
|
|||
)));
|
||||
}
|
||||
|
||||
config.log_build(true).await?;
|
||||
|
||||
Ok(EnclaveBuildResponse.into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
use std::env;
|
||||
use std::{fs, path::Path};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use tokio::process::Command;
|
||||
use cargo_metadata::MetadataCommand;
|
||||
use color_eyre::owo_colors::OwoColorize;
|
||||
use tokio::{
|
||||
process::{Child, Command},
|
||||
sync::watch,
|
||||
};
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::{
|
||||
config::Config,
|
||||
error::Error,
|
||||
handler::{utils::helpers::get_hash_height, Handler},
|
||||
handler::{utils::helpers::write_cache_hash_height, Handler},
|
||||
request::enclave_start::EnclaveStartRequest,
|
||||
response::{enclave_start::EnclaveStartResponse, Response},
|
||||
};
|
||||
|
@ -21,11 +26,13 @@ impl Handler for EnclaveStartRequest {
|
|||
self,
|
||||
config: C,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let mut config = config.as_ref().clone();
|
||||
// Get trusted height and hash
|
||||
let (trusted_height, trusted_hash) = get_hash_height(self.use_latest_trusted, &mut config)?;
|
||||
let config = config.as_ref().clone();
|
||||
info!("{}", "\nPeforming Enclave Start".blue().bold());
|
||||
|
||||
let enclave_dir = config.app_dir.join("enclave");
|
||||
// Get trusted height and hash
|
||||
let (trusted_height, trusted_hash) = self.get_hash_height(&config)?;
|
||||
println!("trusted height: {} hash: {}", trusted_height, trusted_hash);
|
||||
write_cache_hash_height(trusted_height, trusted_hash, &config).await?;
|
||||
|
||||
if config.mock_sgx {
|
||||
let enclave_args: Vec<String> = vec![
|
||||
|
@ -38,65 +45,121 @@ impl Handler for EnclaveStartRequest {
|
|||
];
|
||||
|
||||
// Run quartz enclave and block
|
||||
let _res = run_enclave(
|
||||
enclave_dir.join("Cargo.toml").display().to_string(),
|
||||
config.mock_sgx,
|
||||
enclave_args,
|
||||
let enclave_child =
|
||||
create_mock_enclave_child(config.app_dir.as_path(), config.release, enclave_args)
|
||||
.await?;
|
||||
handle_process(self.shutdown_rx, enclave_child).await?;
|
||||
} else {
|
||||
let enclave_dir = fs::canonicalize(config.app_dir.join("enclave"))?;
|
||||
|
||||
// gramine private key
|
||||
gramine_sgx_gen_private_key(&enclave_dir).await?;
|
||||
|
||||
// gramine manifest
|
||||
let quartz_dir_canon = &enclave_dir.join("..");
|
||||
gramine_manifest(
|
||||
&trusted_height.to_string(),
|
||||
&trusted_hash.to_string(),
|
||||
quartz_dir_canon,
|
||||
&enclave_dir,
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
// set cwd to enclave app
|
||||
env::set_current_dir(enclave_dir).map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
// gramine private key
|
||||
gramine_sgx_gen_private_key().await?;
|
||||
// gramine manifest
|
||||
gramine_manifest(&trusted_height.to_string(), &trusted_hash.to_string()).await?;
|
||||
|
||||
// gramine sign
|
||||
gramine_sgx_sign().await?;
|
||||
gramine_sgx_sign(&enclave_dir).await?;
|
||||
|
||||
// Run quartz enclave and block
|
||||
gramine_sgx().await?;
|
||||
let enclave_child = create_gramine_sgx_child(&enclave_dir).await?;
|
||||
handle_process(self.shutdown_rx, enclave_child).await?;
|
||||
}
|
||||
|
||||
Ok(EnclaveStartResponse.into())
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_enclave(
|
||||
manifest_path: String,
|
||||
mock_sgx: bool,
|
||||
enclave_args: Vec<String>,
|
||||
async fn handle_process(
|
||||
shutdown_rx: Option<watch::Receiver<()>>,
|
||||
mut child: Child,
|
||||
) -> Result<(), Error> {
|
||||
let mut cargo = Command::new("cargo");
|
||||
let command = cargo.args(["run", "--release", "--manifest-path", &manifest_path]);
|
||||
|
||||
if mock_sgx {
|
||||
debug!("Running with mock-sgx enabled");
|
||||
command.arg("--features=mock-sgx");
|
||||
info!("{}", "Running enclave ...".green().bold());
|
||||
match shutdown_rx {
|
||||
Some(mut rx) => {
|
||||
tokio::select! {
|
||||
status = child.wait() => {
|
||||
handle_child_status(status.map_err(|e| Error::GenericErr(e.to_string()))?)?;
|
||||
}
|
||||
|
||||
command.arg("--");
|
||||
command.args(enclave_args);
|
||||
|
||||
println!("command: {:?}", command);
|
||||
|
||||
info!("🚧 Running enclave ...");
|
||||
let status = command
|
||||
.status()
|
||||
_ = rx.changed() => {
|
||||
info!("Enclave shutdown signal received.");
|
||||
let _ = child.kill().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// If no shutdown receiver is provided, just wait for the child process
|
||||
let status = child
|
||||
.wait()
|
||||
.await
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
handle_child_status(status)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn create_mock_enclave_child(
|
||||
app_dir: &Path,
|
||||
release: bool,
|
||||
enclave_args: Vec<String>,
|
||||
) -> Result<Child, Error> {
|
||||
let enclave_dir = app_dir.join("enclave");
|
||||
let target_dir = app_dir.join("target");
|
||||
|
||||
// Use the enclave package metadata to get the path to the program binary
|
||||
let package_name = MetadataCommand::new()
|
||||
.manifest_path(&enclave_dir.join("Cargo.toml"))
|
||||
.exec()
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?
|
||||
.root_package()
|
||||
.ok_or("No root package found in the metadata")
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?
|
||||
.name
|
||||
.clone();
|
||||
|
||||
let executable = if release {
|
||||
target_dir.join("release").join(package_name)
|
||||
} else {
|
||||
target_dir.join("debug").join(package_name)
|
||||
};
|
||||
|
||||
let mut command = Command::new(executable.display().to_string());
|
||||
|
||||
command.args(enclave_args);
|
||||
|
||||
debug!("Enclave Start Command: {:?}", command);
|
||||
|
||||
info!("{}", "🚧 Spawning enclave process ...".green().bold());
|
||||
let child = command
|
||||
.spawn()
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
|
||||
Ok(child)
|
||||
}
|
||||
|
||||
fn handle_child_status(status: std::process::ExitStatus) -> Result<(), Error> {
|
||||
if !status.success() {
|
||||
return Err(Error::GenericErr(format!(
|
||||
"Couldn't build enclave. {:?}",
|
||||
status
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn gramine_sgx_gen_private_key() -> Result<(), Error> {
|
||||
async fn gramine_sgx_gen_private_key(enclave_dir: &Path) -> Result<(), Error> {
|
||||
// Launch the gramine-sgx-gen-private-key command
|
||||
Command::new("gramine-sgx-gen-private-key")
|
||||
.current_dir(enclave_dir)
|
||||
.output()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
|
@ -111,9 +174,12 @@ async fn gramine_sgx_gen_private_key() -> Result<(), Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn gramine_manifest(trusted_height: &str, trusted_hash: &str) -> Result<(), Error> {
|
||||
let current_dir = env::current_dir().map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
|
||||
async fn gramine_manifest(
|
||||
trusted_height: &str,
|
||||
trusted_hash: &str,
|
||||
quartz_dir: &Path,
|
||||
enclave_dir: &Path,
|
||||
) -> Result<(), Error> {
|
||||
let host = target_lexicon::HOST;
|
||||
let arch_libdir = format!(
|
||||
"/lib/{}-{}-{}",
|
||||
|
@ -133,11 +199,12 @@ async fn gramine_manifest(trusted_height: &str, trusted_hash: &str) -> Result<()
|
|||
.arg("-Dra_type=epid")
|
||||
.arg(format!("-Dra_client_spid={}", ra_client_spid))
|
||||
.arg("-Dra_client_linkable=1")
|
||||
.arg(format!("-Dquartz_dir={}", current_dir.display()))
|
||||
.arg(format!("-Dquartz_dir={}", quartz_dir.display().to_string()))
|
||||
.arg(format!("-Dtrusted_height={}", trusted_height))
|
||||
.arg(format!("-Dtrusted_hash={}", trusted_hash))
|
||||
.arg("quartz.manifest.template")
|
||||
.arg("quartz.manifest")
|
||||
.current_dir(enclave_dir)
|
||||
.status()
|
||||
.await
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
|
@ -152,12 +219,13 @@ async fn gramine_manifest(trusted_height: &str, trusted_hash: &str) -> Result<()
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn gramine_sgx_sign() -> Result<(), Error> {
|
||||
async fn gramine_sgx_sign(enclave_dir: &Path) -> Result<(), Error> {
|
||||
let status = Command::new("gramine-sgx-sign")
|
||||
.arg("--manifest")
|
||||
.arg("quartz.manifest")
|
||||
.arg("--output")
|
||||
.arg("quartz.manifest.sgx")
|
||||
.current_dir(enclave_dir)
|
||||
.status()
|
||||
.await
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
|
@ -172,19 +240,13 @@ async fn gramine_sgx_sign() -> Result<(), Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn gramine_sgx() -> Result<(), Error> {
|
||||
let status = Command::new("gramine-sgx")
|
||||
async fn create_gramine_sgx_child(enclave_dir: &Path) -> Result<Child, Error> {
|
||||
info!("🚧 Spawning enclave process ...");
|
||||
|
||||
let child = Command::new("gramine-sgx")
|
||||
.arg("./quartz")
|
||||
.status()
|
||||
.await
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
.current_dir(enclave_dir)
|
||||
.spawn()?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(Error::GenericErr(format!(
|
||||
"gramine-sgx-sign command failed. {:?}",
|
||||
status
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(child)
|
||||
}
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
use std::{env::current_dir, fs, str::FromStr};
|
||||
use std::{fs, str::FromStr};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use async_trait::async_trait;
|
||||
use color_eyre::owo_colors::OwoColorize;
|
||||
use cosmrs::tendermint::chain::Id as ChainId; // TODO see if this redundancy in dependencies can be decreased
|
||||
use cycles_sync::wasmd_client::{CliWasmdClient, WasmdClient};
|
||||
use futures_util::stream::StreamExt;
|
||||
use reqwest::Url;
|
||||
use serde::Serialize;
|
||||
use serde_json::json;
|
||||
use tendermint_rpc::{query::EventType, HttpClient, SubscriptionClient, WebSocketClient};
|
||||
use tm_prover::{config::Config as TmProverConfig, prover::prove};
|
||||
use tracing::{debug, info, trace};
|
||||
use tracing::{debug, info};
|
||||
use wasmd_client::{CliWasmdClient, WasmdClient};
|
||||
|
||||
use super::utils::{
|
||||
helpers::{block_tx_commit, run_relay},
|
||||
types::WasmdTxResponse,
|
||||
};
|
||||
use super::utils::{helpers::block_tx_commit, types::WasmdTxResponse};
|
||||
use crate::{
|
||||
config::Config,
|
||||
error::Error,
|
||||
handler::{
|
||||
utils::{helpers::get_hash_height, types::RelayMessage},
|
||||
utils::{
|
||||
helpers::{read_cached_hash_height, run_relay_rust},
|
||||
types::RelayMessage,
|
||||
},
|
||||
Handler,
|
||||
},
|
||||
request::handshake::HandshakeRequest,
|
||||
|
@ -38,7 +38,7 @@ impl Handler for HandshakeRequest {
|
|||
) -> Result<Self::Response, Self::Error> {
|
||||
let config = config.as_ref().clone();
|
||||
|
||||
trace!("starting handshake...");
|
||||
info!("{}", "\nPeforming Handshake".blue().bold());
|
||||
|
||||
// TODO: may need to import verbosity here
|
||||
let pub_key = handshake(self, config)
|
||||
|
@ -49,25 +49,19 @@ impl Handler for HandshakeRequest {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Message<'a> {
|
||||
message: &'a str,
|
||||
}
|
||||
|
||||
async fn handshake(args: HandshakeRequest, mut config: Config) -> Result<String, anyhow::Error> {
|
||||
async fn handshake(args: HandshakeRequest, config: Config) -> Result<String, anyhow::Error> {
|
||||
let httpurl = Url::parse(&format!("http://{}", config.node_url))?;
|
||||
let wsurl = format!("ws://{}/websocket", config.node_url);
|
||||
|
||||
let tmrpc_client = HttpClient::new(httpurl.as_str())?;
|
||||
let wasmd_client = CliWasmdClient::new(Url::parse(httpurl.as_str())?);
|
||||
|
||||
let (trusted_height, trusted_hash) = get_hash_height(false, &mut config)?;
|
||||
// TODO: dir logic issue #125
|
||||
let base_path = current_dir()?.join("../");
|
||||
let (trusted_height, trusted_hash) = read_cached_hash_height(&config).await?;
|
||||
|
||||
info!("Running SessionCreate");
|
||||
let res: serde_json::Value = run_relay(
|
||||
base_path.as_path(),
|
||||
|
||||
let res: serde_json::Value = run_relay_rust(
|
||||
config.enclave_rpc(),
|
||||
config.mock_sgx,
|
||||
RelayMessage::SessionCreate,
|
||||
)
|
||||
|
@ -94,8 +88,7 @@ async fn handshake(args: HandshakeRequest, mut config: Config) -> Result<String,
|
|||
info!("Waiting 2 blocks for light client proof");
|
||||
two_block_waitoor(&wsurl).await?;
|
||||
|
||||
// TODO: dir logic issue #125
|
||||
let proof_path = current_dir()?.join("../utils/tm-prover/light-client-proof.json");
|
||||
let proof_path = config.cache_dir()?.join("light-client-proof.json");
|
||||
debug!("Proof path: {:?}", proof_path.to_str());
|
||||
|
||||
// Call tm prover with trusted hash and height
|
||||
|
@ -118,16 +111,13 @@ async fn handshake(args: HandshakeRequest, mut config: Config) -> Result<String,
|
|||
|
||||
// Read proof file
|
||||
let proof = fs::read_to_string(proof_path.as_path())?;
|
||||
let proof_json = serde_json::to_string(&Message {
|
||||
message: proof.trim(),
|
||||
})?;
|
||||
|
||||
// Execute SessionSetPubKey on enclave
|
||||
info!("Running SessionSetPubKey");
|
||||
let res: serde_json::Value = run_relay(
|
||||
base_path.as_path(),
|
||||
let res: serde_json::Value = run_relay_rust(
|
||||
config.enclave_rpc(),
|
||||
config.mock_sgx,
|
||||
RelayMessage::SessionSetPubKey(proof_json),
|
||||
RelayMessage::SessionSetPubKey(proof.trim().to_string()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
|
@ -2,8 +2,9 @@ use std::path::PathBuf;
|
|||
|
||||
use async_trait::async_trait;
|
||||
use cargo_generate::{generate, GenerateArgs, TemplatePath, Vcs};
|
||||
use color_eyre::owo_colors::OwoColorize;
|
||||
use tokio::fs;
|
||||
use tracing::trace;
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
config::Config,
|
||||
|
@ -18,13 +19,13 @@ impl Handler for InitRequest {
|
|||
type Error = Error;
|
||||
type Response = Response;
|
||||
|
||||
// TODO: Add non-template init method
|
||||
async fn handle<C: AsRef<Config> + Send>(
|
||||
self,
|
||||
config: C,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let config = config.as_ref();
|
||||
|
||||
trace!("initializing directory structure...");
|
||||
info!("{}", "\nPeforming Init".blue().bold());
|
||||
|
||||
let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..");
|
||||
|
||||
|
@ -61,6 +62,7 @@ impl Handler for InitRequest {
|
|||
.display()
|
||||
.to_string();
|
||||
|
||||
info!("\n{}", "It's TEE time.".green().bold());
|
||||
Ok(InitResponse { result_dir }.into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,38 @@
|
|||
use std::{path::Path, time::Duration};
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use cosmrs::{AccountId, ErrorReport};
|
||||
use cycles_sync::wasmd_client::{CliWasmdClient, WasmdClient};
|
||||
use cosmwasm_std::Binary;
|
||||
use hex::decode;
|
||||
use quartz_common::{
|
||||
contract::msg::{
|
||||
execute::{session_create::RawSessionCreate, session_set_pub_key::RawSessionSetPubKey},
|
||||
instantiate::RawCoreInstantiate,
|
||||
},
|
||||
proto::{
|
||||
core_client::CoreClient, InstantiateRequest, SessionCreateRequest, SessionSetPubKeyRequest,
|
||||
},
|
||||
};
|
||||
use quartz_tee_ra::{intel_sgx::epid::types::ReportBody, IASReport};
|
||||
use regex::Regex;
|
||||
use reqwest::Url;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_json::json;
|
||||
use subtle_encoding::bech32::decode as bech32_decode;
|
||||
use tendermint::{block::Height, Hash};
|
||||
use tendermint_rpc::{
|
||||
endpoint::tx::Response as TmTxResponse, error::ErrorDetail, Client, HttpClient,
|
||||
};
|
||||
use tokio::{fs, process::Command};
|
||||
use tokio::{
|
||||
fs::{self, File},
|
||||
io::AsyncWriteExt,
|
||||
process::Command,
|
||||
};
|
||||
use tracing::debug;
|
||||
use wasmd_client::{CliWasmdClient, WasmdClient};
|
||||
|
||||
use super::types::RelayMessage;
|
||||
use crate::{config::Config, error};
|
||||
use crate::{config::Config, error::Error};
|
||||
|
||||
pub fn wasmaddr_to_id(address_str: &str) -> Result<AccountId, anyhow::Error> {
|
||||
let (hr, _) = bech32_decode(address_str).map_err(|e| anyhow!(e))?;
|
||||
|
@ -26,36 +43,6 @@ pub fn wasmaddr_to_id(address_str: &str) -> Result<AccountId, anyhow::Error> {
|
|||
address_str.parse().map_err(|e: ErrorReport| anyhow!(e))
|
||||
}
|
||||
|
||||
// TODO: move wrapping result with "quartz:" struct into here
|
||||
pub async fn run_relay<R: DeserializeOwned>(
|
||||
base_path: &Path,
|
||||
mock_sgx: bool,
|
||||
msg: RelayMessage,
|
||||
) -> Result<R, anyhow::Error> {
|
||||
let relayer_path = base_path.join("cli/scripts/relay.sh");
|
||||
|
||||
let mut bash = Command::new("bash");
|
||||
let command = bash
|
||||
.arg(relayer_path)
|
||||
.arg(msg.to_string())
|
||||
.env("MOCK_SGX", mock_sgx.to_string());
|
||||
|
||||
if let RelayMessage::SessionSetPubKey(proof) = msg {
|
||||
command.arg(proof);
|
||||
}
|
||||
|
||||
let output = command.output().await?;
|
||||
|
||||
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")?;
|
||||
|
@ -90,37 +77,15 @@ pub async fn block_tx_commit(client: &HttpClient, tx: Hash) -> Result<TmTxRespon
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the trusted hash and height
|
||||
pub fn get_hash_height(
|
||||
use_latest: bool,
|
||||
config: &mut Config,
|
||||
) -> Result<(Height, Hash), error::Error> {
|
||||
if use_latest || config.trusted_height == 0 || config.trusted_hash.is_empty() {
|
||||
let (trusted_height, trusted_hash) = latest_height_hash(&config.node_url)?;
|
||||
config.trusted_hash = trusted_hash.to_string();
|
||||
config.trusted_height = trusted_height.into();
|
||||
|
||||
Ok((trusted_height, trusted_hash))
|
||||
} else {
|
||||
Ok((
|
||||
config.trusted_height.try_into()?,
|
||||
config
|
||||
.trusted_hash
|
||||
.parse()
|
||||
.map_err(|_| error::Error::GenericErr("invalid hash".to_string()))?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Queries the chain for the latested height and hash
|
||||
pub fn latest_height_hash(node_url: &String) -> Result<(Height, Hash), error::Error> {
|
||||
pub fn query_latest_height_hash(node_url: &String) -> Result<(Height, Hash), Error> {
|
||||
let httpurl = Url::parse(&format!("http://{}", node_url))
|
||||
.map_err(|e| error::Error::GenericErr(e.to_string()))?;
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
let wasmd_client = CliWasmdClient::new(httpurl);
|
||||
|
||||
let (trusted_height, trusted_hash) = wasmd_client
|
||||
.trusted_height_hash()
|
||||
.map_err(|e| error::Error::GenericErr(e.to_string()))?;
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
|
||||
Ok((
|
||||
trusted_height.try_into()?,
|
||||
|
@ -128,17 +93,223 @@ pub fn latest_height_hash(node_url: &String) -> Result<(Height, Hash), error::Er
|
|||
))
|
||||
}
|
||||
|
||||
pub async fn persist_config_hash_height(config: &Config) -> Result<(), error::Error> {
|
||||
let config_path = config.app_dir.join("quartz.toml");
|
||||
pub async fn write_cache_hash_height(
|
||||
trusted_height: Height,
|
||||
trusted_hash: Hash,
|
||||
config: &Config,
|
||||
) -> Result<(), Error> {
|
||||
let height_path = config.cache_dir()?.join("trusted.height");
|
||||
fs::write(height_path.as_path(), trusted_height.to_string()).await?;
|
||||
|
||||
let toml_content = fs::read_to_string(&config_path).await?;
|
||||
let mut written_config: Config = toml::from_str(&toml_content)?;
|
||||
|
||||
written_config.trusted_hash.clone_from(&config.trusted_hash);
|
||||
written_config.trusted_height = config.trusted_height;
|
||||
|
||||
let toml_string = toml::to_string(config)?;
|
||||
fs::write(&config_path, toml_string).await?;
|
||||
let hash_path = config.cache_dir()?.join("trusted.hash");
|
||||
fs::write(hash_path.as_path(), trusted_hash.to_string()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn read_cached_hash_height(config: &Config) -> Result<(Height, Hash), Error> {
|
||||
let height_path = config.cache_dir()?.join("trusted.height");
|
||||
let hash_path = config.cache_dir()?.join("trusted.hash");
|
||||
|
||||
if !height_path.exists() {
|
||||
return Err(Error::PathNotFile(height_path.display().to_string()));
|
||||
}
|
||||
if !hash_path.exists() {
|
||||
return Err(Error::PathNotFile(hash_path.display().to_string()));
|
||||
}
|
||||
|
||||
let trusted_height: Height = fs::read_to_string(height_path.as_path()).await?.parse()?;
|
||||
let trusted_hash: Hash = fs::read_to_string(hash_path.as_path()).await?.parse()?;
|
||||
|
||||
Ok((trusted_height, trusted_hash))
|
||||
}
|
||||
|
||||
async fn run_docker_command(quote: &[u8]) -> Result<(String, String), Error> {
|
||||
let dir = tempfile::tempdir()?;
|
||||
let ias_api_key: &str = "669244b3e6364b5888289a11d2a1726d";
|
||||
let ra_client_spid: &str = "51CAF5A48B450D624AEFE3286D314894";
|
||||
let quote_file_path = dir.path().join("test.quote");
|
||||
let datareport_file_path = dir.path().join("datareport");
|
||||
let datareportsig_file_path = dir.path().join("datareportsig");
|
||||
|
||||
let mut quote_file = File::create(quote_file_path.clone()).await?;
|
||||
quote_file.write_all(quote).await?;
|
||||
|
||||
let status = Command::new("docker")
|
||||
.arg("run")
|
||||
.arg("--rm")
|
||||
.arg("-it")
|
||||
.arg("-v")
|
||||
.arg("/tmp:/tmp:rw")
|
||||
.arg("gramineproject/gramine:1.7-jammy")
|
||||
.arg(format!(
|
||||
"gramine-sgx-ias-request report -g \"{}\" -k \"{}\" -q \"{}\" -r \"{}\" -s \"{}\" > /dev/null 2>&1",
|
||||
ra_client_spid, ias_api_key, quote_file_path.display().to_string(), datareport_file_path.display().to_string(), datareportsig_file_path.display().to_string()
|
||||
))
|
||||
.status()
|
||||
.await
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(Error::GenericErr(
|
||||
"Failed to run docker command".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let report = fs::read_to_string(datareport_file_path)
|
||||
.await
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
|
||||
let reportsig = fs::read_to_string(datareportsig_file_path)
|
||||
.await
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?
|
||||
.replace("\r", "");
|
||||
|
||||
Ok((report, reportsig))
|
||||
}
|
||||
|
||||
// TODO: move wrapping result with "quartz:" struct into here
|
||||
pub async fn run_relay_rust<R: DeserializeOwned>(
|
||||
enclave_rpc: String,
|
||||
mock_sgx: bool,
|
||||
relay_msg: RelayMessage,
|
||||
) -> Result<R, anyhow::Error> {
|
||||
// Query the gRPC quartz enclave service
|
||||
let mut qc_client = CoreClient::connect(enclave_rpc).await?;
|
||||
|
||||
let attested_msg = match &relay_msg {
|
||||
RelayMessage::Instantiate => &qc_client
|
||||
.instantiate(tonic::Request::new(InstantiateRequest {}))
|
||||
.await?
|
||||
.get_ref()
|
||||
.message
|
||||
.clone(),
|
||||
RelayMessage::SessionCreate => &qc_client
|
||||
.session_create(tonic::Request::new(SessionCreateRequest {}))
|
||||
.await?
|
||||
.get_ref()
|
||||
.message
|
||||
.clone(),
|
||||
RelayMessage::SessionSetPubKey(proof) => &qc_client
|
||||
.session_set_pub_key(SessionSetPubKeyRequest {
|
||||
message: proof.to_string(),
|
||||
})
|
||||
.await?
|
||||
.get_ref()
|
||||
.message
|
||||
.clone(),
|
||||
};
|
||||
let mut attested_msg_json: serde_json::Value = serde_json::from_str(attested_msg)?;
|
||||
let quote = attested_msg_json["quote"].take();
|
||||
|
||||
if mock_sgx {
|
||||
match relay_msg {
|
||||
RelayMessage::Instantiate => {
|
||||
// Construct CoreInstantiate
|
||||
let msg: RawCoreInstantiate = serde_json::from_value(attested_msg_json)?;
|
||||
|
||||
let query_result: R = serde_json::from_value(json!({
|
||||
"msg": RawCoreInstantiate::from(msg),
|
||||
"attestation": quote
|
||||
}))?;
|
||||
|
||||
return Ok(query_result);
|
||||
}
|
||||
RelayMessage::SessionCreate => {
|
||||
// Convert RelayMessage to a snake_case string
|
||||
let request_key = relay_msg.to_string();
|
||||
|
||||
let msg: RawSessionCreate = serde_json::from_value(attested_msg_json)?;
|
||||
// Build the nested JSON structures
|
||||
let jsonify = json!({
|
||||
"quartz": {
|
||||
request_key: {
|
||||
"msg": msg,
|
||||
"attestation": quote
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let query_result: R = serde_json::from_value(jsonify)?;
|
||||
|
||||
return Ok(query_result);
|
||||
}
|
||||
RelayMessage::SessionSetPubKey(_) => {
|
||||
// Convert RelayMessage to a snake_case string
|
||||
let request_key = relay_msg.to_string();
|
||||
|
||||
let msg: RawSessionSetPubKey = serde_json::from_value(attested_msg_json)?;
|
||||
// Build the nested JSON structures
|
||||
let jsonify = json!({
|
||||
"quartz": {
|
||||
request_key: {
|
||||
"msg": msg,
|
||||
"attestation": quote
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let query_result: R = serde_json::from_value(jsonify)?;
|
||||
|
||||
return Ok(query_result);
|
||||
}
|
||||
}
|
||||
}
|
||||
// else
|
||||
// docker
|
||||
let quote_str = quote
|
||||
.as_str()
|
||||
.ok_or_else(|| Error::GenericErr("quote is not a string".to_string()))?;
|
||||
|
||||
let quote = decode(quote_str).map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||
println!("quote: {:?}", quote_str);
|
||||
let (report, report_sig) = run_docker_command("e).await?;
|
||||
|
||||
match relay_msg {
|
||||
RelayMessage::Instantiate => {
|
||||
let msg: RawCoreInstantiate = serde_json::from_value(attested_msg_json)?;
|
||||
let report_json: ReportBody = serde_json::from_str(&report)?;
|
||||
let report_sig = report_sig.replace('\n', "");
|
||||
|
||||
let ias_report = IASReport {
|
||||
report: report_json,
|
||||
report_sig: Binary::from_base64(&report_sig)?,
|
||||
};
|
||||
|
||||
// Nest the report inside another "report" field
|
||||
let jsonify = json!({
|
||||
"msg": msg,
|
||||
"attestation": {
|
||||
"report": {
|
||||
"report": json!(ias_report.report),
|
||||
"reportsig": ias_report.report_sig
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let query_result: R = serde_json::from_value(jsonify)?;
|
||||
|
||||
return Ok(query_result);
|
||||
}
|
||||
RelayMessage::SessionCreate | RelayMessage::SessionSetPubKey(_) => {
|
||||
// Convert request to snake_case
|
||||
let request_key = relay_msg.to_string();
|
||||
|
||||
// Construct the JSON structure for SessionCreate or SessionSetPubKey
|
||||
let jsonify = json!({
|
||||
"quartz": {
|
||||
request_key: {
|
||||
"msg": attested_msg_json,
|
||||
"attestation": {
|
||||
"report": report,
|
||||
"reportsig": report_sig
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let query_result: R = serde_json::from_value(jsonify)?;
|
||||
return Ok(query_result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,9 +49,9 @@ pub enum RelayMessage {
|
|||
impl std::fmt::Display for RelayMessage {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
RelayMessage::Instantiate => write!(f, "Instantiate"),
|
||||
RelayMessage::SessionCreate => write!(f, "SessionCreate"),
|
||||
RelayMessage::SessionSetPubKey(_) => write!(f, "SessionSetPubKey"),
|
||||
RelayMessage::Instantiate => write!(f, "instantiate"),
|
||||
RelayMessage::SessionCreate => write!(f, "session_create"),
|
||||
RelayMessage::SessionSetPubKey(_) => write!(f, "session_set_pub_key"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
unused_qualifications
|
||||
)]
|
||||
|
||||
pub mod cache;
|
||||
pub mod cli;
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
|
@ -24,7 +25,7 @@ use std::path::PathBuf;
|
|||
|
||||
use clap::Parser;
|
||||
use cli::ToFigment;
|
||||
use color_eyre::eyre::Result;
|
||||
use color_eyre::{eyre::Result, owo_colors::OwoColorize};
|
||||
use config::Config;
|
||||
use figment::{
|
||||
providers::{Env, Format, Serialized, Toml},
|
||||
|
@ -50,7 +51,7 @@ const BANNER: &str = r"
|
|||
async fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
|
||||
println!("{BANNER}");
|
||||
println!("{}", BANNER.yellow().bold());
|
||||
|
||||
let args: Cli = Cli::parse();
|
||||
check_path(&args.app_dir)?;
|
||||
|
|
|
@ -3,13 +3,14 @@ use crate::{
|
|||
error::Error,
|
||||
request::{
|
||||
contract_build::ContractBuildRequest, contract_deploy::ContractDeployRequest,
|
||||
enclave_build::EnclaveBuildRequest, enclave_start::EnclaveStartRequest,
|
||||
dev::DevRequest, enclave_build::EnclaveBuildRequest, enclave_start::EnclaveStartRequest,
|
||||
handshake::HandshakeRequest, init::InitRequest,
|
||||
},
|
||||
};
|
||||
|
||||
pub mod contract_build;
|
||||
pub mod contract_deploy;
|
||||
pub mod dev;
|
||||
pub mod enclave_build;
|
||||
pub mod enclave_start;
|
||||
pub mod handshake;
|
||||
|
@ -23,6 +24,7 @@ pub enum Request {
|
|||
ContractDeploy(ContractDeployRequest),
|
||||
EnclaveBuild(EnclaveBuildRequest),
|
||||
EnclaveStart(EnclaveStartRequest),
|
||||
Dev(DevRequest),
|
||||
}
|
||||
|
||||
impl TryFrom<Command> for Request {
|
||||
|
@ -33,10 +35,21 @@ impl TryFrom<Command> for Request {
|
|||
Command::Init(args) => Ok(InitRequest { name: args.name }.try_into()?),
|
||||
Command::Handshake(args) => Ok(HandshakeRequest {
|
||||
contract: args.contract,
|
||||
use_latest_trusted: args.use_latest_trusted,
|
||||
}
|
||||
.into()),
|
||||
Command::Contract { contract_command } => contract_command.try_into(),
|
||||
Command::Enclave { enclave_command } => enclave_command.try_into(),
|
||||
Command::Dev(args) => Ok(DevRequest {
|
||||
watch: args.watch,
|
||||
use_latest_trusted: args.use_latest_trusted,
|
||||
init_msg: serde_json::from_str(&args.contract_deploy.init_msg)
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?,
|
||||
label: args.contract_deploy.label,
|
||||
contract_manifest: args.contract_deploy.contract_manifest,
|
||||
release: args.enclave_build.release,
|
||||
}
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,25 +60,29 @@ impl TryFrom<ContractCommand> for Request {
|
|||
fn try_from(cmd: ContractCommand) -> Result<Request, Error> {
|
||||
match cmd {
|
||||
ContractCommand::Deploy(args) => {
|
||||
if !args.wasm_bin_path.exists() {
|
||||
return Err(Error::PathNotFile(args.wasm_bin_path.display().to_string()));
|
||||
if !args.contract_manifest.exists() {
|
||||
return Err(Error::PathNotFile(
|
||||
args.contract_manifest.display().to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(ContractDeployRequest {
|
||||
init_msg: serde_json::from_str(&args.init_msg)
|
||||
.map_err(|e| Error::GenericErr(e.to_string()))?,
|
||||
label: args.label,
|
||||
wasm_bin_path: args.wasm_bin_path,
|
||||
contract_manifest: args.contract_manifest,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
ContractCommand::Build(args) => {
|
||||
if !args.manifest_path.exists() {
|
||||
return Err(Error::PathNotFile(args.manifest_path.display().to_string()));
|
||||
if !args.contract_manifest.exists() {
|
||||
return Err(Error::PathNotFile(
|
||||
args.contract_manifest.display().to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(ContractBuildRequest {
|
||||
manifest_path: args.manifest_path,
|
||||
contract_manifest: args.contract_manifest,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
@ -79,10 +96,11 @@ impl TryFrom<EnclaveCommand> for Request {
|
|||
fn try_from(cmd: EnclaveCommand) -> Result<Request, Error> {
|
||||
match cmd {
|
||||
EnclaveCommand::Build(args) => Ok(EnclaveBuildRequest {
|
||||
manifest_path: args.manifest_path,
|
||||
release: args.release,
|
||||
}
|
||||
.into()),
|
||||
EnclaveCommand::Start(args) => Ok(EnclaveStartRequest {
|
||||
shutdown_rx: None,
|
||||
use_latest_trusted: args.use_latest_trusted,
|
||||
}
|
||||
.into()),
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::request::Request;
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ContractBuildRequest {
|
||||
pub manifest_path: PathBuf,
|
||||
pub contract_manifest: PathBuf,
|
||||
}
|
||||
|
||||
impl From<ContractBuildRequest> for Request {
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::{error::Error, request::Request};
|
|||
pub struct ContractDeployRequest {
|
||||
pub init_msg: serde_json::Value,
|
||||
pub label: String,
|
||||
pub wasm_bin_path: PathBuf,
|
||||
pub contract_manifest: PathBuf,
|
||||
}
|
||||
|
||||
impl From<ContractDeployRequest> for Request {
|
||||
|
|
19
cli/src/request/dev.rs
Normal file
19
cli/src/request/dev.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use crate::request::Request;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DevRequest {
|
||||
pub watch: bool,
|
||||
pub use_latest_trusted: bool,
|
||||
pub init_msg: serde_json::Value,
|
||||
pub label: String,
|
||||
pub contract_manifest: PathBuf,
|
||||
pub release: bool,
|
||||
}
|
||||
|
||||
impl From<DevRequest> for Request {
|
||||
fn from(request: DevRequest) -> Self {
|
||||
Self::Dev(request)
|
||||
}
|
||||
}
|
|
@ -1,10 +1,8 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use crate::request::Request;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EnclaveBuildRequest {
|
||||
pub manifest_path: PathBuf,
|
||||
pub release: bool,
|
||||
}
|
||||
|
||||
impl From<EnclaveBuildRequest> for Request {
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
use crate::request::Request;
|
||||
use tendermint::{block::Height, Hash};
|
||||
use tokio::sync::watch;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
config::Config, error::Error, handler::utils::helpers::query_latest_height_hash,
|
||||
request::Request,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EnclaveStartRequest {
|
||||
pub shutdown_rx: Option<watch::Receiver<()>>,
|
||||
pub use_latest_trusted: bool,
|
||||
}
|
||||
|
||||
|
@ -10,3 +18,24 @@ impl From<EnclaveStartRequest> for Request {
|
|||
Self::EnclaveStart(request)
|
||||
}
|
||||
}
|
||||
|
||||
impl EnclaveStartRequest {
|
||||
/// Returns the trusted hash and height
|
||||
pub fn get_hash_height(&self, config: &Config) -> Result<(Height, Hash), Error> {
|
||||
if self.use_latest_trusted || config.trusted_height == 0 || config.trusted_hash.is_empty() {
|
||||
debug!("querying latest trusted hash & height from node");
|
||||
let (trusted_height, trusted_hash) = query_latest_height_hash(&config.node_url)?;
|
||||
|
||||
Ok((trusted_height, trusted_hash))
|
||||
} else {
|
||||
debug!("reusing config trusted hash & height");
|
||||
Ok((
|
||||
config.trusted_height.try_into()?,
|
||||
config
|
||||
.trusted_hash
|
||||
.parse()
|
||||
.map_err(|_| Error::GenericErr("invalid hash".to_string()))?,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::request::Request;
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct HandshakeRequest {
|
||||
pub contract: AccountId,
|
||||
pub use_latest_trusted: bool,
|
||||
}
|
||||
|
||||
impl From<HandshakeRequest> for Request {
|
||||
|
|
|
@ -2,12 +2,13 @@ use serde::Serialize;
|
|||
|
||||
use crate::response::{
|
||||
contract_build::ContractBuildResponse, contract_deploy::ContractDeployResponse,
|
||||
enclave_build::EnclaveBuildResponse, enclave_start::EnclaveStartResponse,
|
||||
dev::DevResponse, enclave_build::EnclaveBuildResponse, enclave_start::EnclaveStartResponse,
|
||||
handshake::HandshakeResponse, init::InitResponse,
|
||||
};
|
||||
|
||||
pub mod contract_build;
|
||||
pub mod contract_deploy;
|
||||
pub mod dev;
|
||||
pub mod enclave_build;
|
||||
pub mod enclave_start;
|
||||
pub mod handshake;
|
||||
|
@ -21,4 +22,5 @@ pub enum Response {
|
|||
ContractDeploy(ContractDeployResponse),
|
||||
EnclaveBuild(EnclaveBuildResponse),
|
||||
EnclaveStart(EnclaveStartResponse),
|
||||
Dev(DevResponse),
|
||||
}
|
||||
|
|
12
cli/src/response/dev.rs
Normal file
12
cli/src/response/dev.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
use serde::Serialize;
|
||||
|
||||
use crate::response::Response;
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct DevResponse;
|
||||
|
||||
impl From<DevResponse> for Response {
|
||||
fn from(response: DevResponse) -> Self {
|
||||
Self::Dev(response)
|
||||
}
|
||||
}
|
20
cosmwasm/packages/wasmd-client/Cargo.toml
Normal file
20
cosmwasm/packages/wasmd-client/Cargo.toml
Normal file
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "wasmd-client"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
[lib]
|
||||
path="src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
cosmrs.workspace = true
|
||||
hex.workspace = true
|
||||
reqwest.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
|
@ -244,9 +244,11 @@ impl WasmdClient for CliWasmdClient {
|
|||
serde_json::from_slice(&output.stdout).unwrap_or_default();
|
||||
|
||||
let trusted_height = query_result["SyncInfo"]["latest_block_height"]
|
||||
.as_u64()
|
||||
.as_str()
|
||||
.ok_or(anyhow!("Could not query height"))?;
|
||||
|
||||
let trusted_height = trusted_height.parse::<u64>()?;
|
||||
|
||||
let trusted_hash = query_result["SyncInfo"]["latest_block_hash"]
|
||||
.as_str()
|
||||
.ok_or(anyhow!("Could not query height"))?
|
|
@ -9,13 +9,6 @@ repository.workspace = true
|
|||
keywords = ["blockchain", "cosmos", "tendermint", "cycles", "quartz"]
|
||||
readme = "README.md"
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "submit"
|
||||
path = "src/bin/submit.rs"
|
||||
|
||||
[dependencies]
|
||||
# external
|
||||
async-trait.workspace = true
|
||||
|
@ -41,6 +34,8 @@ cosmwasm-std.workspace = true
|
|||
|
||||
# quartz
|
||||
cw-tee-mtcs = { path = "../../apps/mtcs/contracts/cw-tee-mtcs/" }
|
||||
wasmd-client.workspace = true
|
||||
mtcs-enclave.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
rand_core.workspace = true
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
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())
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use cosmrs::{tendermint::chain::Id, AccountId};
|
||||
use displaydoc::Display;
|
||||
use reqwest::Url;
|
||||
use subtle_encoding::{bech32::decode as bech32_decode, Error as Bech32DecodeError};
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
const ADDRESS_PREFIX: &str = "wasm";
|
||||
|
||||
#[derive(Clone, Debug, Parser)]
|
||||
#[command(author, version, about)]
|
||||
pub struct Cli {
|
||||
/// Increase output logging verbosity to debug level.
|
||||
#[arg(short, long)]
|
||||
pub verbose: bool,
|
||||
|
||||
/// The host to which to bind the API server.
|
||||
#[arg(short = 'N', long, default_value = "http://127.0.0.1:26657")]
|
||||
pub node: Url,
|
||||
|
||||
/// Obligato API server.
|
||||
#[arg(long, default_value = "https://bisenzone.obligato.network")]
|
||||
pub obligato_url: Url,
|
||||
|
||||
/// Obligato key.
|
||||
#[arg(
|
||||
long,
|
||||
default_value = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNhZXNzdGpjdG16bXVqaW55cGJlIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTcwMzAxOTE0OSwiZXhwIjoyMDE4NTk1MTQ5fQ.EV6v5J3dz8WHAdTK4_IEisKzF-n1Gqyn4wCce_Zrqf4"
|
||||
)]
|
||||
pub obligato_key: String,
|
||||
|
||||
/// Path to output CSV file
|
||||
#[arg(short, long)]
|
||||
pub keys_file: PathBuf,
|
||||
|
||||
/// Path to obligation-user map
|
||||
#[arg(short, long)]
|
||||
pub obligation_user_map_file: PathBuf,
|
||||
|
||||
/// Chain-id of MTCS chain
|
||||
#[arg(long, default_value = "testing")]
|
||||
pub chain_id: Id,
|
||||
|
||||
/// Smart contract address
|
||||
#[arg(short, long, value_parser = wasm_address)]
|
||||
pub contract: AccountId,
|
||||
|
||||
/// tx sender address
|
||||
#[arg(short, long)]
|
||||
pub user: String,
|
||||
|
||||
/// Main command
|
||||
#[command(subcommand)]
|
||||
pub command: CliCommand,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Subcommand)]
|
||||
pub enum CliCommand {
|
||||
/// Sync obligations
|
||||
SyncObligations {
|
||||
/// epoch pk
|
||||
#[arg(short, long)]
|
||||
epoch_pk: String,
|
||||
/// liquidity sources' UUIDs
|
||||
#[arg(short, long, num_args = 1.., value_parser = parse_uuid)]
|
||||
liquidity_sources: Vec<Uuid>,
|
||||
},
|
||||
/// Sync set-offs
|
||||
SyncSetOffs,
|
||||
/// Get address for Uuid
|
||||
GetAddress {
|
||||
#[arg(long, value_parser = parse_uuid)]
|
||||
uuid: Uuid,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Display, Error, Debug)]
|
||||
pub enum AddressError {
|
||||
/// Address is not bech32 encoded
|
||||
NotBech32Encoded(#[source] Bech32DecodeError),
|
||||
/// Human readable part mismatch (expected `wasm`, found {0})
|
||||
HumanReadableMismatch(String),
|
||||
}
|
||||
|
||||
fn wasm_address(address_str: &str) -> Result<AccountId, AddressError> {
|
||||
let (hr, _) = bech32_decode(address_str).map_err(AddressError::NotBech32Encoded)?;
|
||||
if hr != ADDRESS_PREFIX {
|
||||
return Err(AddressError::HumanReadableMismatch(hr));
|
||||
}
|
||||
|
||||
Ok(address_str.parse().unwrap())
|
||||
}
|
||||
|
||||
fn parse_uuid(uuid_str: &str) -> Result<Uuid, String> {
|
||||
Uuid::parse_str(uuid_str).map_err(|e| e.to_string())
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
pub mod cli;
|
||||
pub mod obligato_client;
|
||||
pub mod types;
|
||||
pub mod wasmd_client;
|
|
@ -1,237 +1,129 @@
|
|||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
error::Error,
|
||||
fs::File,
|
||||
io::{BufReader, BufWriter, Write},
|
||||
path::PathBuf,
|
||||
str::FromStr,
|
||||
};
|
||||
use std::str::FromStr;
|
||||
|
||||
use bip32::{
|
||||
secp256k1::{
|
||||
use anyhow::anyhow;
|
||||
use bip32::secp256k1::{
|
||||
ecdsa::VerifyingKey,
|
||||
sha2::{Digest, Sha256},
|
||||
},
|
||||
Error as Bip32Error, Language, Mnemonic, Prefix, PrivateKey, Seed, XPrv,
|
||||
};
|
||||
use clap::Parser;
|
||||
use cosmrs::{tendermint::account::Id as TmAccountId, AccountId};
|
||||
use cosmwasm_std::HexBinary;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use tracing::{debug, Level};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
cli::{Cli, CliCommand},
|
||||
obligato_client::{http::HttpClient, Client},
|
||||
types::{
|
||||
Obligation, ObligatoObligation, ObligatoSetOff, RawEncryptedObligation, RawObligation,
|
||||
RawOffset, RawSetOff, SubmitObligatioMsg, SubmitObligatoObligationsMsgInner,
|
||||
},
|
||||
wasmd_client::{CliWasmdClient, QueryResult, WasmdClient},
|
||||
use cosmrs::{tendermint::chain::Id as TmChainId, AccountId};
|
||||
use cosmwasm_std::{Addr, HexBinary};
|
||||
use cw_tee_mtcs::state::{LiquiditySource, LiquiditySourceType};
|
||||
use mtcs_enclave::types::{
|
||||
ContractObligation, RawEncryptedObligation, RawSetOff, SubmitObligationsMsg,
|
||||
SubmitObligationsMsgInner,
|
||||
};
|
||||
use reqwest::Url;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use subtle_encoding::bech32::decode as bech32_decode;
|
||||
use wasmd_client::{CliWasmdClient, WasmdClient};
|
||||
|
||||
mod cli;
|
||||
mod obligato_client;
|
||||
mod types;
|
||||
mod wasmd_client;
|
||||
|
||||
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 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];
|
||||
|
||||
type DynError = Box<dyn Error>;
|
||||
|
||||
#[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<(), DynError> {
|
||||
async fn main() -> Result<(), anyhow::Error> {
|
||||
let cli = Cli::parse();
|
||||
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(if cli.verbose {
|
||||
Level::DEBUG
|
||||
} else {
|
||||
Level::ERROR
|
||||
})
|
||||
.with_level(true)
|
||||
.with_writer(std::io::stderr)
|
||||
.init();
|
||||
let mut alice = Addr::unchecked("wasm124tuy67a9dcvfgcr4gjmz60syd8ddaugl33v0n");
|
||||
let mut bob = Addr::unchecked("wasm1ctkqmg45u85jnf5ur9796h7ze4hj6ep5y7m7l6");
|
||||
let overdraft = Addr::unchecked(cli.overdraft);
|
||||
|
||||
match cli.command {
|
||||
CliCommand::SyncObligations {
|
||||
ref epoch_pk,
|
||||
ref liquidity_sources,
|
||||
} => sync_obligations(cli.clone(), epoch_pk, liquidity_sources).await?,
|
||||
CliCommand::SyncSetOffs => sync_setoffs(cli).await?,
|
||||
CliCommand::GetAddress { uuid } => address_from_uuid(uuid)?,
|
||||
if cli.flip {
|
||||
let temp = alice.clone();
|
||||
alice = bob;
|
||||
bob = temp;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn address_from_uuid(uuid: Uuid) -> Result<(), DynError> {
|
||||
let seed = global_seed()?;
|
||||
let sk = derive_child_xprv(&seed, uuid);
|
||||
let pk_b = sk.public_key().public_key().to_sec1_bytes();
|
||||
let pk = VerifyingKey::from_sec1_bytes(&pk_b)?;
|
||||
println!("{}", wasm_address(pk));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn wasm_address(pk: VerifyingKey) -> String {
|
||||
let tm_pk = TmAccountId::from(pk);
|
||||
AccountId::new(ADDRESS_PREFIX, tm_pk.as_bytes())
|
||||
.unwrap()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn global_seed() -> Result<Seed, Bip32Error> {
|
||||
let mnemonic = Mnemonic::new(MNEMONIC_PHRASE, Language::English)?;
|
||||
Ok(mnemonic.to_seed("password"))
|
||||
}
|
||||
|
||||
async fn sync_setoffs(cli: Cli) -> Result<(), DynError> {
|
||||
let wasmd_client = CliWasmdClient::new(cli.node);
|
||||
let query_result: QueryResult<QueryAllSetoffsResponse> =
|
||||
wasmd_client.query_smart(&cli.contract, json!("get_all_setoffs"))?;
|
||||
let setoffs = query_result.data.setoffs;
|
||||
|
||||
// read keys
|
||||
let keys = read_keys_file(cli.keys_file)?;
|
||||
let obligation_user_map = read_obligation_user_map_file(cli.obligation_user_map_file)?;
|
||||
|
||||
let setoffs: Vec<ObligatoSetOff> = setoffs
|
||||
.iter()
|
||||
.flat_map(|(obligation_digest, so)| match so {
|
||||
RawSetOff::SetOff(sos_enc) => {
|
||||
let so_enc = sos_enc.first().unwrap();
|
||||
let (debtor_id, creditor_id) =
|
||||
obligation_user_map.get(obligation_digest).copied().unwrap();
|
||||
|
||||
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()) {
|
||||
so
|
||||
} else if let Ok(so) = ecies::decrypt(&sk(creditor_id), so_enc.as_slice()) {
|
||||
so
|
||||
} else {
|
||||
unreachable!()
|
||||
let alice_to_bob: ContractObligation = ContractObligation {
|
||||
debtor: alice.clone(),
|
||||
creditor: bob.clone(),
|
||||
amount: 10,
|
||||
salt: HexBinary::from([0; 64]),
|
||||
};
|
||||
|
||||
let so: RawOffset = serde_json::from_slice(&so_ser).unwrap();
|
||||
Some(ObligatoSetOff {
|
||||
debtor_id,
|
||||
creditor_id,
|
||||
amount: so.set_off,
|
||||
})
|
||||
}
|
||||
RawSetOff::Transfer(_) => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
debug!("setoffs: {setoffs:?}");
|
||||
|
||||
// send to Obligato
|
||||
let client = HttpClient::new(cli.obligato_url, cli.obligato_key);
|
||||
client.set_setoffs(setoffs).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn sync_obligations(
|
||||
cli: Cli,
|
||||
epoch_pk: &str,
|
||||
liquidity_sources: &[Uuid],
|
||||
) -> Result<(), DynError> {
|
||||
let mut intents = {
|
||||
let client = HttpClient::new(cli.obligato_url.clone(), cli.obligato_key);
|
||||
client
|
||||
.get_obligations()
|
||||
.await
|
||||
.map_err(|_| cli.obligato_url.to_string())?
|
||||
let bob_acceptance: ContractObligation = ContractObligation {
|
||||
debtor: bob.clone(),
|
||||
creditor: overdraft.clone(),
|
||||
amount: 10,
|
||||
salt: HexBinary::from([0; 64]),
|
||||
};
|
||||
|
||||
let keys = derive_keys(&mut intents, liquidity_sources)?;
|
||||
write_keys_to_file(cli.keys_file, &keys);
|
||||
|
||||
add_default_acceptances(&mut intents, liquidity_sources);
|
||||
|
||||
debug!("intents: {intents:?}");
|
||||
|
||||
let intents_enc = {
|
||||
let epoch_pk = VerifyingKey::from_sec1_bytes(&hex::decode(epoch_pk).unwrap()).unwrap();
|
||||
encrypt_intents(intents, &keys, &epoch_pk, cli.obligation_user_map_file)
|
||||
let alice_tender: ContractObligation = ContractObligation {
|
||||
debtor: overdraft.clone(),
|
||||
creditor: alice.clone(),
|
||||
amount: 10,
|
||||
salt: HexBinary::from([0; 64]),
|
||||
};
|
||||
debug!("Encrypted {} intents", intents_enc.len());
|
||||
|
||||
let liquidity_sources = liquidity_sources
|
||||
.iter()
|
||||
.map(|id| keys[id].private_key().public_key())
|
||||
.collect();
|
||||
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 wasmd_client = CliWasmdClient::new(cli.node);
|
||||
wasmd_client.tx_execute(&cli.contract, &cli.chain_id, 3000000, &cli.user, msg)?;
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
fn create_wasm_msg(
|
||||
obligations_enc: Vec<(Sha256Digest, Vec<u8>)>,
|
||||
liquidity_sources: Vec<VerifyingKey>,
|
||||
) -> Result<serde_json::Value, DynError> {
|
||||
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 liquidity_sources = liquidity_sources
|
||||
.into_iter()
|
||||
.map(|pk| HexBinary::from(pk.to_sec1_bytes().as_ref()))
|
||||
.collect();
|
||||
|
||||
let msg = SubmitObligatioMsg {
|
||||
submit_obligations: SubmitObligatoObligationsMsgInner {
|
||||
obligations: obligations_enc,
|
||||
liquidity_sources,
|
||||
},
|
||||
};
|
||||
serde_json::to_value(msg).map_err(Into::into)
|
||||
pub struct OverdraftObligation {
|
||||
pub debtor: Addr,
|
||||
pub creditor: Addr,
|
||||
pub amount: u64,
|
||||
}
|
||||
|
||||
fn encrypt_intents(
|
||||
intents: Vec<ObligatoObligation>,
|
||||
keys: &HashMap<Uuid, XPrv>,
|
||||
fn encrypt_overdraft_intents(
|
||||
intents: Vec<ContractObligation>,
|
||||
epoch_pk: &VerifyingKey,
|
||||
obligation_user_map_file: PathBuf,
|
||||
) -> Vec<(Sha256Digest, Vec<u8>)> {
|
||||
let mut intents_enc = vec![];
|
||||
let mut intent_user_map = HashMap::new();
|
||||
|
||||
for i in intents {
|
||||
// create an intent
|
||||
let ro = {
|
||||
let o = Obligation {
|
||||
debtor: keys[&i.debtor_id].private_key().public_key(),
|
||||
creditor: keys[&i.creditor_id].private_key().public_key(),
|
||||
amount: i.amount,
|
||||
salt: [0; 64],
|
||||
};
|
||||
RawObligation::from(o)
|
||||
};
|
||||
|
||||
// serialize intent
|
||||
let i_ser = serde_json::to_string(&ro).unwrap();
|
||||
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();
|
||||
|
@ -244,167 +136,38 @@ fn encrypt_intents(
|
|||
};
|
||||
|
||||
intents_enc.push((i_digest, i_cipher));
|
||||
intent_user_map.insert(HexBinary::from(i_digest), (i.debtor_id, i.creditor_id));
|
||||
}
|
||||
|
||||
write_obligation_user_map_to_file(obligation_user_map_file, &intent_user_map);
|
||||
|
||||
intents_enc
|
||||
}
|
||||
|
||||
fn add_default_acceptances(obligations: &mut Vec<ObligatoObligation>, liquidity_sources: &[Uuid]) {
|
||||
let acceptances = obligations.iter().fold(HashSet::new(), |mut acc, o| {
|
||||
if !liquidity_sources.contains(&o.debtor_id) {
|
||||
for ls in liquidity_sources {
|
||||
let acceptance = ObligatoObligation {
|
||||
id: Default::default(),
|
||||
debtor_id: o.creditor_id,
|
||||
creditor_id: *ls,
|
||||
amount: u32::MAX as u64,
|
||||
};
|
||||
acc.insert(acceptance);
|
||||
}
|
||||
}
|
||||
acc
|
||||
});
|
||||
|
||||
obligations.extend(acceptances.into_iter().collect::<Vec<_>>());
|
||||
}
|
||||
|
||||
fn read_keys_file(keys_file: PathBuf) -> Result<HashMap<Uuid, XPrv>, DynError> {
|
||||
let keys_file = File::open(keys_file)?;
|
||||
let keys_reader = BufReader::new(keys_file);
|
||||
let keys: HashMap<Uuid, String> = serde_json::from_reader(keys_reader)?;
|
||||
Ok(keys
|
||||
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(|(id, key_str)| (id, XPrv::from_str(&key_str).unwrap()))
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn write_keys_to_file(output_file: PathBuf, keys: &HashMap<Uuid, XPrv>) {
|
||||
let keys_str: HashMap<_, _> = keys
|
||||
.iter()
|
||||
.map(|(id, k)| (id, k.to_string(Prefix::XPRV).to_string()))
|
||||
.map(|(digest, ciphertext)| {
|
||||
let digest = HexBinary::from(digest);
|
||||
let ciphertext = HexBinary::from(ciphertext);
|
||||
RawEncryptedObligation { digest, ciphertext }
|
||||
})
|
||||
.collect();
|
||||
|
||||
let output_file = File::create(output_file).expect("create file");
|
||||
let mut output_reader = BufWriter::new(output_file);
|
||||
output_reader
|
||||
.write_all(serde_json::to_string(&keys_str).unwrap().as_bytes())
|
||||
.expect("write file");
|
||||
let msg = SubmitObligationsMsg {
|
||||
submit_obligations: SubmitObligationsMsgInner {
|
||||
obligations: obligations_enc,
|
||||
liquidity_sources,
|
||||
},
|
||||
};
|
||||
serde_json::to_value(msg).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn read_obligation_user_map_file(
|
||||
file: PathBuf,
|
||||
) -> Result<HashMap<HexBinary, (Uuid, Uuid)>, DynError> {
|
||||
let map_file = File::open(file)?;
|
||||
let map_reader = BufReader::new(map_file);
|
||||
serde_json::from_reader(map_reader).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));
|
||||
}
|
||||
|
||||
fn write_obligation_user_map_to_file(
|
||||
output_file: PathBuf,
|
||||
obligation_user_map: &HashMap<HexBinary, (Uuid, Uuid)>,
|
||||
) {
|
||||
let output_file = File::create(output_file).expect("create file");
|
||||
let mut output_reader = BufWriter::new(output_file);
|
||||
output_reader
|
||||
.write_all(
|
||||
serde_json::to_string(&obligation_user_map)
|
||||
.unwrap()
|
||||
.as_bytes(),
|
||||
)
|
||||
.expect("write file");
|
||||
}
|
||||
|
||||
fn derive_keys(
|
||||
obligations: &mut Vec<ObligatoObligation>,
|
||||
liquidity_sources: &[Uuid],
|
||||
) -> Result<HashMap<Uuid, XPrv>, DynError> {
|
||||
// Derive a BIP39 seed value using the given password
|
||||
let seed = global_seed()?;
|
||||
|
||||
obligations.sort_by_key(|o| o.debtor_id);
|
||||
|
||||
let mut keys = HashMap::new();
|
||||
|
||||
for ls in liquidity_sources {
|
||||
keys.entry(*ls)
|
||||
.or_insert_with(|| derive_child_xprv(&seed, *ls));
|
||||
}
|
||||
|
||||
for o in obligations {
|
||||
keys.entry(o.debtor_id)
|
||||
.or_insert_with(|| derive_child_xprv(&seed, o.debtor_id));
|
||||
keys.entry(o.creditor_id)
|
||||
.or_insert_with(|| derive_child_xprv(&seed, o.creditor_id));
|
||||
}
|
||||
|
||||
Ok(keys)
|
||||
}
|
||||
|
||||
fn derive_child_xprv(seed: &Seed, uuid: Uuid) -> XPrv {
|
||||
// Hash the UUID using SHA-256
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(uuid.as_bytes());
|
||||
let uuid_digest = hasher.finalize();
|
||||
|
||||
// Convert the hash bytes to a number
|
||||
let uuid_digest_num = u128::from_be_bytes(uuid_digest[..16].try_into().unwrap());
|
||||
|
||||
// Take modulo (2^31 - 1)
|
||||
let address_index = uuid_digest_num % ((1u128 << 31) - 1);
|
||||
|
||||
let child_path = format!("m/0/44'/118'/0'/0/{address_index}")
|
||||
.parse()
|
||||
.unwrap();
|
||||
let child_xprv = XPrv::derive_from_path(seed, &child_path);
|
||||
child_xprv.unwrap()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{error::Error, str::FromStr};
|
||||
|
||||
use bip32::{Mnemonic, Prefix, PrivateKey, XPrv};
|
||||
use rand_core::OsRng;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{derive_child_xprv, global_seed};
|
||||
|
||||
#[test]
|
||||
fn test_create_mnemonic() {
|
||||
// Generate random Mnemonic using the default language (English)
|
||||
let mnemonic = Mnemonic::random(OsRng, Default::default());
|
||||
println!("{}", mnemonic.phrase());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enc_dec_for_derived() -> Result<(), Box<dyn Error>> {
|
||||
let seed = global_seed()?;
|
||||
|
||||
let alice_uuid = Uuid::from_u128(1);
|
||||
let alice_sk = derive_child_xprv(&seed, alice_uuid);
|
||||
let alice_pk = alice_sk.private_key().public_key();
|
||||
|
||||
assert_eq!(
|
||||
alice_pk.to_sec1_bytes(),
|
||||
hex::decode("0219b0b8ee5fe9b317b69119fd15170d79737380c4f020e251b7839096f5513ccf")
|
||||
.unwrap()
|
||||
.into()
|
||||
);
|
||||
|
||||
let alice_sk_str = alice_sk.to_string(Prefix::XPRV).to_string();
|
||||
assert_eq!(XPrv::from_str(&alice_sk_str).unwrap(), alice_sk);
|
||||
|
||||
let msg = r#"{"debtor":"02027e3510f66f1f6c1ea5e3600062255928e518220f7883810cac3fc7fc092057","creditor":"0216254f4636c4e68ae22d98538851a46810b65162fe37bf57cba6d563617c913e","amount":10,"salt":"65c188bcc133add598f7eecc449112f4bf61024345316cff0eb5ce61291991b141073dcd3c543ea142e66fffa8f483dc382043d37e490ef9b8069c489ce94a0b"}"#;
|
||||
let ciphertext = ecies::encrypt(&alice_pk.to_sec1_bytes(), msg.as_bytes()).unwrap();
|
||||
// println!("{}", hex::encode(&ciphertext));
|
||||
|
||||
let msg_dec =
|
||||
ecies::decrypt(&alice_sk.private_key().to_bytes(), ciphertext.as_slice()).unwrap();
|
||||
assert_eq!(msg, String::from_utf8(msg_dec).unwrap().as_str());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(address_str.parse().unwrap())
|
||||
}
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
use async_trait::async_trait;
|
||||
use reqwest::Url;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
obligato_client::Client,
|
||||
types::{ObligatoObligation, ObligatoSetOff},
|
||||
};
|
||||
|
||||
pub struct HttpClient {
|
||||
client: reqwest::Client,
|
||||
url: Url,
|
||||
key: String,
|
||||
}
|
||||
|
||||
impl HttpClient {
|
||||
pub fn new(url: Url, key: String) -> Self {
|
||||
Self {
|
||||
client: reqwest::Client::new(),
|
||||
url,
|
||||
key,
|
||||
}
|
||||
}
|
||||
|
||||
fn url_with_path(&self, path: &str) -> Url {
|
||||
let mut url = self.url.clone();
|
||||
url.set_path(path);
|
||||
url
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Client for HttpClient {
|
||||
type Error = reqwest::Error;
|
||||
|
||||
async fn get_obligations(&self) -> Result<Vec<ObligatoObligation>, Self::Error> {
|
||||
let response = self
|
||||
.client
|
||||
.post(self.url_with_path("api/sync/obligations2contract"))
|
||||
.json(&json!({"denom_id": "1", "key": self.key }))
|
||||
.send()
|
||||
.await?
|
||||
.json::<GetObligationsResponse>()
|
||||
.await?;
|
||||
|
||||
Ok(response.all_obligations.obligations)
|
||||
}
|
||||
|
||||
async fn set_setoffs(&self, setoffs: Vec<ObligatoSetOff>) -> Result<(), Self::Error> {
|
||||
let response = self
|
||||
.client
|
||||
.post(self.url_with_path("api/set-offs"))
|
||||
.json(&setoffs)
|
||||
.send()
|
||||
.await?;
|
||||
debug!("{}", response.text().await.unwrap());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
pub struct GetObligationsInnerResponse {
|
||||
obligations: Vec<ObligatoObligation>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
pub struct GetObligationsResponse {
|
||||
#[serde(rename = "allObligations")]
|
||||
all_obligations: GetObligationsInnerResponse,
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
use async_trait::async_trait;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
obligato_client::Client,
|
||||
types::{ObligatoObligation, ObligatoSetOff},
|
||||
};
|
||||
|
||||
pub struct MockClient {
|
||||
pub bank: Uuid,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Client for MockClient {
|
||||
type Error = ();
|
||||
|
||||
async fn get_obligations(&self) -> Result<Vec<ObligatoObligation>, Self::Error> {
|
||||
Ok(vec![
|
||||
// obligation: 1 --10--> 2
|
||||
ObligatoObligation {
|
||||
id: Uuid::from_u128(1),
|
||||
debtor_id: Uuid::from_u128(1),
|
||||
creditor_id: Uuid::from_u128(2),
|
||||
amount: 10,
|
||||
},
|
||||
// tender: $ --10--> 1
|
||||
ObligatoObligation {
|
||||
id: Uuid::from_u128(2),
|
||||
debtor_id: self.bank,
|
||||
creditor_id: Uuid::from_u128(1),
|
||||
amount: 10,
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
async fn set_setoffs(&self, setoffs: Vec<ObligatoSetOff>) -> Result<(), Self::Error> {
|
||||
println!("{:?}", setoffs);
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::types::{ObligatoObligation, ObligatoSetOff};
|
||||
|
||||
pub mod http;
|
||||
pub mod mock;
|
||||
|
||||
#[async_trait]
|
||||
pub trait Client {
|
||||
type Error;
|
||||
|
||||
async fn get_obligations(&self) -> Result<Vec<ObligatoObligation>, Self::Error>;
|
||||
|
||||
async fn set_setoffs(&self, setoffs: Vec<ObligatoSetOff>) -> Result<(), Self::Error>;
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
use bip32::secp256k1::ecdsa::VerifyingKey;
|
||||
use cosmwasm_std::{Addr, HexBinary};
|
||||
use cw_tee_mtcs::state::LiquiditySource;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Eq, Hash)]
|
||||
pub struct ObligatoObligation {
|
||||
pub id: Uuid,
|
||||
pub debtor_id: Uuid,
|
||||
pub creditor_id: Uuid,
|
||||
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)]
|
||||
pub struct RawObligation {
|
||||
pub debtor: HexBinary,
|
||||
pub creditor: HexBinary,
|
||||
pub amount: u64,
|
||||
#[serde(default)]
|
||||
pub salt: HexBinary,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Obligation {
|
||||
pub debtor: VerifyingKey,
|
||||
pub creditor: VerifyingKey,
|
||||
pub amount: u64,
|
||||
pub salt: [u8; 64],
|
||||
}
|
||||
|
||||
impl From<Obligation> for RawObligation {
|
||||
fn from(obligation: Obligation) -> Self {
|
||||
Self {
|
||||
debtor: obligation.debtor.to_sec1_bytes().into_vec().into(),
|
||||
creditor: obligation.creditor.to_sec1_bytes().into_vec().into(),
|
||||
amount: obligation.amount,
|
||||
salt: obligation.salt.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
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<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 liquidity_sources: Vec<HexBinary>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
pub struct ObligatoSetOff {
|
||||
pub debtor_id: Uuid,
|
||||
pub creditor_id: Uuid,
|
||||
pub amount: u64,
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
|
@ -76,6 +76,7 @@ pub async fn prove(
|
|||
.latest_trusted()
|
||||
.ok_or_else(|| eyre!("No trusted state found for primary"))?;
|
||||
|
||||
info!("Getting status of node");
|
||||
let status = client.status().await?;
|
||||
let latest_height = status.sync_info.latest_block_height;
|
||||
let latest_app_hash = status.sync_info.latest_app_hash;
|
||||
|
|
Loading…
Reference in a new issue