From ba19eb4ec1309fc56134bae4cc542e3df8b442d8 Mon Sep 17 00:00:00 2001 From: ghe0 Date: Sun, 16 Mar 2025 22:28:19 +0200 Subject: [PATCH] code migration --- .gitignore | 2 + Cargo.lock | 4457 ++++++++++++++++++++ Cargo.toml | 40 + README.md | 50 + build.rs | 19 + docker/Dockerfile | 8 + docker/create_image.sh | 18 + rustfmt.toml | 3 + samples/dtrfs/sample.yaml | 4 + samples/new_app/new_app_deploy_config.yaml | 11 + samples/new_app/new_app_launch_config.yaml | 15 + samples/new_vm/advanced_os_config.yaml | 29 + samples/new_vm/no_public_ips.yaml | 13 + samples/new_vm/public_ipv4_and_ipv6.yaml | 15 + samples/new_vm/specific_city.yaml | 14 + scripts/detee-cli_injector.sh | 209 + scripts/install.sh | 96 + src/bin/detee-cli.rs | 756 ++++ src/bin/super-detee-cli.rs | 154 + src/config.rs | 442 ++ src/constants.rs | 1 + src/lib.rs | 50 + src/name_generator.rs | 202 + src/operators.rs | 92 + src/packagers.rs | 50 + src/sgx/cli_handler.rs | 189 + src/sgx/config.rs | 34 + src/sgx/grpc_brain.rs | 89 + src/sgx/grpc_dtpm.rs | 106 + src/sgx/mod.rs | 165 + src/sgx/packaging.rs | 36 + src/snp/deploy.rs | 183 + src/snp/grpc.rs | 375 ++ src/snp/injector.rs | 147 + src/snp/mod.rs | 468 ++ src/snp/update.rs | 98 + src/utils.rs | 114 + vm.proto | 271 ++ 38 files changed, 9025 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 build.rs create mode 100644 docker/Dockerfile create mode 100755 docker/create_image.sh create mode 100644 rustfmt.toml create mode 100644 samples/dtrfs/sample.yaml create mode 100644 samples/new_app/new_app_deploy_config.yaml create mode 100644 samples/new_app/new_app_launch_config.yaml create mode 100644 samples/new_vm/advanced_os_config.yaml create mode 100644 samples/new_vm/no_public_ips.yaml create mode 100644 samples/new_vm/public_ipv4_and_ipv6.yaml create mode 100644 samples/new_vm/specific_city.yaml create mode 100755 scripts/detee-cli_injector.sh create mode 100755 scripts/install.sh create mode 100644 src/bin/detee-cli.rs create mode 100644 src/bin/super-detee-cli.rs create mode 100644 src/config.rs create mode 100644 src/constants.rs create mode 100644 src/lib.rs create mode 100644 src/name_generator.rs create mode 100644 src/operators.rs create mode 100644 src/packagers.rs create mode 100644 src/sgx/cli_handler.rs create mode 100644 src/sgx/config.rs create mode 100644 src/sgx/grpc_brain.rs create mode 100644 src/sgx/grpc_dtpm.rs create mode 100644 src/sgx/mod.rs create mode 100644 src/sgx/packaging.rs create mode 100644 src/snp/deploy.rs create mode 100644 src/snp/grpc.rs create mode 100644 src/snp/injector.rs create mode 100644 src/snp/mod.rs create mode 100644 src/snp/update.rs create mode 100644 src/utils.rs create mode 100644 vm.proto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c919d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +tmp diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2a0d454 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4457 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "alloy-json-abi" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4012581681b186ba0882007ed873987cc37f86b1b488fe6b91d5efd0b585dc41" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-primitives" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478bedf4d24e71ea48428d1bc278553bd7c6ae07c30ca063beb0b09fe58a9e74" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "foldhash", + "hashbrown 0.15.2", + "indexmap 2.7.0", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand", + "ruint", + "rustc-hash 2.1.1", + "serde", + "sha3", + "tiny-keccak", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6c1d995bff8d011f7cd6c81820d51825e6e06d6db73914c1630ecf544d83d6" +dependencies = [ + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2708e27f58d747423ae21d31b7a6625159bd8d867470ddd0256f396a68efa11" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6b7984d7e085dec382d2c5ef022b533fcdb1fe6129200af30ebf5afddb6a361" +dependencies = [ + "alloy-sol-macro-input", + "const-hex", + "heck 0.5.0", + "indexmap 2.7.0", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.91", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d6a9fc4ed1a3c70bdb2357bec3924551c1a59f24e5a04a74472c755b37f87d" +dependencies = [ + "const-hex", + "dunce", + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.91", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1b3e9a48a6dd7bb052a111c8d93b5afc7956ed5e2cb4177793dc63bb1d2a36" +dependencies = [ + "serde", + "winnow", +] + +[[package]] +name = "alloy-sol-types" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6044800da35c38118fd4b98e18306bd3b91af5dedeb54c1b768cf1b4fb68f549" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "asn1" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3ecbce89a22627b5e8e6e11d69715617138290289e385cde773b1fe50befdb" +dependencies = [ + "asn1_derive", +] + +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive 0.4.0", + "asn1-rs-impl 0.1.0", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "asn1-rs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +dependencies = [ + "asn1-rs-derive 0.5.1", + "asn1-rs-impl 0.2.0", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure 0.12.6", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", + "synstructure 0.13.1", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "asn1_derive" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "861af988fac460ac69a09f41e6217a8fb9178797b76fcc9478444be6a59be19c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "async-compression" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "zstd", + "zstd-safe", +] + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "auto_impl" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12882f59de5360c748c4cbf569a042d5fb0eb515f7bea9c1f470b47f6ffbd73" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "aws-lc-rs" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b7ddaa2c56a367ad27a094ad8ef4faacf8a617c2575acb2ba88949df999ca" +dependencies = [ + "aws-lc-sys", + "paste", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54ac4f13dad353b209b34cbec082338202cbc01c8f00336b55c750c13ac91f8f" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", + "paste", +] + +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower 0.5.2", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.91", + "which", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "bytecount" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +dependencies = [ + "serde", +] + +[[package]] +name = "cc" +version = "1.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4730490333d58093109dc02c23174c3f4d490998c3fed3cc8e82d57afedb9cf" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_complete" +version = "4.5.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375f9d8255adeeedd51053574fd8d4ba875ea5fa558e86617b07f09f1680c8b6" +dependencies = [ + "clap", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "const-hex" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0485bab839b018a8f1723fc5391819fea5f8f0f32288ef8a735fd096b6160c" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version 0.4.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "data-encoding" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" + +[[package]] +name = "dcap-rs" +version = "0.1.0" +source = "git+https://github.com/automata-network/dcap-rs.git#4c162176961276986cf6f242487e3a8c519c74fc" +dependencies = [ + "alloy-sol-types", + "chrono", + "hex", + "p256", + "serde", + "serde_json", + "sha2", + "sha3", + "time", + "x509-parser 0.15.1", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs 0.5.2", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "der-parser" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +dependencies = [ + "asn1-rs 0.6.2", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", + "unicode-xid", +] + +[[package]] +name = "detee-cli" +version = "0.1.0" +dependencies = [ + "bs58", + "chrono", + "clap", + "clap_complete", + "detee-sgx", + "detee-shared", + "ed25519-dalek", + "env_logger", + "hex", + "hyper", + "hyper-rustls", + "lazy_static", + "log", + "openssl", + "prost", + "prost-types", + "rand", + "rand_core", + "reqwest", + "rustls", + "serde", + "serde_json", + "serde_yaml", + "tabled", + "thiserror 2.0.11", + "tokio", + "tokio-stream", + "tonic", + "tonic-build", + "tower 0.5.2", +] + +[[package]] +name = "detee-qvl" +version = "0.1.0" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-qvl?branch=dcap-rs#afbe18fad06d3304277c402e6b0695fa09837136" +dependencies = [ + "chrono", + "dcap-rs", + "futures", + "home", + "lazy_static", + "log", + "reqwest", + "serde", + "serde_json", + "sgx_pck_extension", + "tokio", + "twox-hash", + "ureq", + "url", + "x509-parser 0.16.0", +] + +[[package]] +name = "detee-sgx" +version = "0.1.0" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-sgx?branch=hratls#a323dfb3f9d05517022678ed3ac39fc83023083d" +dependencies = [ + "base64", + "detee-qvl", + "hex", + "hyper", + "hyper-rustls", + "hyper-util", + "lazy_static", + "log", + "prost", + "rcgen", + "ring", + "rustls", + "tokio-rustls", + "tonic-build", + "tower 0.5.2", + "tower-http", + "x509-parser 0.16.0", +] + +[[package]] +name = "detee-shared" +version = "0.1.0" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#099f0a0488bce8e59c9c9e9a5e9b1f24998f1633" +dependencies = [ + "base64", + "prost", + "serde", + "serde_yaml", + "thiserror 2.0.11", + "tonic", + "tonic-build", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pem-rfc7468", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "h2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.7.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", + "serde", +] + +[[package]] +name = "hdrhistogram" +version = "7.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +dependencies = [ + "byteorder", + "num-traits", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", + "serde", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0f0a572e8ffe56e2ff4f769f32ffe919282c3916799f8b68688b6030063bea" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "multimap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" + +[[package]] +name = "native-tls" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework 2.11.1", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs 0.5.2", +] + +[[package]] +name = "oid-registry" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" +dependencies = [ + "asn1-rs 0.6.2", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "openssl" +version = "0.10.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-src" +version = "300.4.2+3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168ce4e058f975fe43e89d9ccf78ca668601887ae736090aacc23ae353c298e2" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "papergrid" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b0f8def1f117e13c895f3eda65a7b5650688da29d6ad04635f61bc7b92eebd" +dependencies = [ + "bytecount", + "fnv", + "unicode-width", +] + +[[package]] +name = "parity-scale-codec" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64", + "serde", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" +dependencies = [ + "memchr", + "thiserror 2.0.11", + "ucd-trie", +] + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.7.0", +] + +[[package]] +name = "pin-project" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn 2.0.91", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "prost" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b" +dependencies = [ + "heck 0.5.0", + "itertools 0.13.0", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 2.0.91", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" +dependencies = [ + "anyhow", + "itertools 0.13.0", + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "prost-types" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc" +dependencies = [ + "prost", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "serde", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rcgen" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" +dependencies = [ + "pem", + "ring", + "rustls-pki-types", + "time", + "yasna", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower 0.5.2", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "ruint" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "825df406ec217a8116bd7b06897c6cc8f65ffefc15d030ae2c9540acc9ed50b6" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp 0.3.1", + "fastrlp 0.4.0", + "num-bigint", + "num-integer", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.24", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.2.0", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags", + "core-foundation 0.10.0", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" + +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "serde_json" +version = "1.0.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.7.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sgx_pck_extension" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a922f73ebff18858d5096dc718915677de5fe6e5a6fc4d9344f7bf0781672f" +dependencies = [ + "asn1", + "asn1-rs 0.5.2", + "thiserror 1.0.69", + "x509-parser 0.15.1", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sha3-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +dependencies = [ + "cc", + "cfg-if", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c2de690018098e367beeb793991c7d4dc7270f42c9d2ac4ccc876c1368ca430" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tabled" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6709222f3973137427ce50559cd564dc187a95b9cfe01613d2f4e93610e510a" +dependencies = [ + "papergrid", + "tabled_derive", +] + +[[package]] +name = "tabled_derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "931be476627d4c54070a1f3a9739ccbfec9b36b39815106a20cce2243bbcefe1" +dependencies = [ + "heck 0.4.1", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" +dependencies = [ + "cfg-if", + "fastrand", + "getrandom 0.3.1", + "once_cell", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "time" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +dependencies = [ + "indexmap 2.7.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tonic" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "socket2", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "hdrhistogram", + "indexmap 2.7.0", + "pin-project-lite", + "slab", + "sync_wrapper", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" +dependencies = [ + "async-compression", + "base64", + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "iri-string", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower 0.5.2", + "tower-layer", + "tower-service", + "tracing", + "uuid", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "twox-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7b17f197b3050ba473acf9181f7b1d3b66d1cf7356c6cc57886662276e65908" +dependencies = [ + "rand", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "217751151c53226090391713e533d9a5e904ba2570dabaaace29032687589c3e" +dependencies = [ + "base64", + "cc", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "ureq-proto", + "utf-8", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c51fe73e1d8c4e06bb2698286f7e7453c6fc90528d6d2e7fc36bb4e87fe09b1" +dependencies = [ + "base64", + "http", + "httparse", + "log", +] + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" +dependencies = [ + "getrandom 0.3.1", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.91", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x509-parser" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" +dependencies = [ + "asn1-rs 0.5.2", + "data-encoding", + "der-parser 8.2.0", + "lazy_static", + "nom", + "oid-registry 0.6.1", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "x509-parser" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +dependencies = [ + "asn1-rs 0.6.2", + "data-encoding", + "der-parser 9.0.0", + "lazy_static", + "nom", + "oid-registry 0.7.1", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", + "synstructure 0.13.1", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", + "synstructure 0.13.1", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "zstd" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..92accd7 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "detee-cli" +version = "0.1.0" +edition = "2021" + +[dependencies] +clap = "4.5.29" +clap_complete = "4.5.44" +ed25519-dalek = { version = "2.1.1", features = ["rand_core"] } +env_logger = "0.11.6" +lazy_static = "1.5.0" +log = "0.4.22" +prost = "0.13.4" +prost-types = "0.13.4" +rand = "0.8.5" +rand_core = { version = "0.6.4", features = ["alloc", "getrandom", "std"] } +serde = { version = "1.0.216", features = ["derive"] } +serde_yaml = "0.9.34" +tabled = "0.17.0" +tokio-stream = "0.1.17" +tokio = { version = "1.42.0", features = ["macros", "rt-multi-thread"] } +tonic = "0.12" +thiserror = "2.0.9" +bs58 = "0.5.1" +chrono = "0.4.39" +reqwest = {version = "0.12.12", features = ["blocking"] } +serde_json = "1.0.139" +hex = "0.4.3" +hyper = "1.6.0" +rustls = "0.23.23" +tower = "0.5.2" +hyper-rustls = "0.27.5" +openssl = { version = "0.10.71", features = ["vendored"] } +detee-sgx = { git = "ssh://git@gitea.detee.cloud/noormohammedb/detee-sgx", branch = "hratls", features=["hratls", "qvl"] } + +detee-shared = { git = "ssh://git@gitea.detee.cloud/noormohammedb/detee-shared", branch = "stable_01" } +# detee-shared = { path = "../detee-shared" } + +[build-dependencies] +tonic-build = "0.12" diff --git a/README.md b/README.md new file mode 100644 index 0000000..76d0b73 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# DeTEE CLI + +The DeTEE CLI will allow you to create VMs and containers on the [DeTEE decentralized cloud network](https://detee.ltd/). +All services running on this network are protected through Trusted Execution Environments (TEEs), meaning +that the hardware provide will not be able to modify the software or read the data. + +### Installing the DeTEE CLI + +To use the CLI, just start a container: +``` +mkdir -p ~/.detee/container_volume/cli +mkdir -p ~/.detee/container_volume/.ssh +docker run --pull always -dt --name detee-cli \ + --volume ~/.detee/container_volume/cli:/root/.detee/cli:rw \ + --volume ~/.detee/container_volume/.ssh:/root/.ssh:rw \ + --entrypoint /usr/bin/fish detee/detee-cli:latest +``` + +The commands above will create a container that will stay alive on your system, +and persistent relevant account data. Any time you want to use the CLI, run: +``` +docker exec -it detee-cli fish +``` + +### First time configuration +Inside the container, configure the accounts for your session: +``` +ssh-keygen +# Hit enter a few times. +detee-cli account ssh-pubkey-path /root/.ssh/id_ed25519.pub +detee-cli account brain-url http://164.92.249.180:31337 +``` + +### VM creation + +Before creating a VM, you will need Loyalty Points from DeTEE. During the testnet phase, these are airdropped to active community members. If you don't have any points, make sure you contact the team the [Discord server](https://discord.gg/DcfYczAMtD). + +To deploy a VM: +``` +# arch +detee-cli vm deploy +# ubuntu +detee-cli vm deploy --distro ubuntu +# fedora +detee-cli vm deploy --distro fedora +``` + +## Resources +* DeTEE CLI docs: [https://docs.detee.ltd/cloud/cli.html](https://docs.detee.ltd/cloud/cli.html). +* TEE security proof: [https://detee.ltd/hacker-challenge](https://detee.ltd/hacker-challenge). diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..8e52a04 --- /dev/null +++ b/build.rs @@ -0,0 +1,19 @@ +fn main() { + tonic_build::configure() + .type_attribute(".vm_proto.VmContract", "#[derive(serde::Serialize, serde::Deserialize)]") + .type_attribute( + ".vm_proto.InspectOperatorResp", + "#[derive(serde::Serialize, serde::Deserialize)]", + ) + .type_attribute( + ".vm_proto.ListOperatorsResp", + "#[derive(serde::Serialize, serde::Deserialize)]", + ) + .type_attribute( + ".vm_proto.VmNodeListResp", + "#[derive(serde::Serialize, serde::Deserialize)]", + ) + .build_server(true) + .compile_protos(&["vm.proto"], &["proto"]) + .unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e)); +} diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..240b1cc --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,8 @@ +from archlinux:latest +copy tmp/.detee /root/.detee +run pacman -Syu --noconfirm +run pacman -S fish neovim openssh python3 --noconfirm +run mkdir -p /root/.config/fish/completions/ +run mv /root/.detee/bin /usr/local/ +run mv /root/.detee/deps /usr/local/ +run mv /root/.detee/detee-cli.fish /root/.config/fish/completions/detee-cli.fish diff --git a/docker/create_image.sh b/docker/create_image.sh new file mode 100755 index 0000000..f73000b --- /dev/null +++ b/docker/create_image.sh @@ -0,0 +1,18 @@ +#!/bin/bash +cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" +scriptdir="$(pwd)" +mkdir "${scriptdir}/tmp" + +cd "${scriptdir}/.." +./scripts/install.sh +mkdir -p "${scriptdir}/tmp/.detee/cli/vms" +touch "${scriptdir}/tmp/.detee/cli/vms/uuid_list" +cp -r "${HOME}/.detee/artefacts" "${scriptdir}/tmp/.detee" +cp -r "${HOME}/.detee/bin" "${scriptdir}/tmp/.detee" +cp -r "${HOME}/.detee/deps" "${scriptdir}/tmp/.detee" +cp -r "${HOME}/.detee/samples" "${scriptdir}/tmp/.detee" +${HOME}/.detee/bin/detee-cli completion fish > "${scriptdir}/tmp/.detee/detee-cli.fish" + +cd "${scriptdir}" +docker pull archlinux:latest +docker build -t detee/detee-cli:latest . diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..a484977 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,3 @@ +reorder_impl_items = true +use_small_heuristics = "Max" +imports_granularity = "Crate" diff --git a/samples/dtrfs/sample.yaml b/samples/dtrfs/sample.yaml new file mode 100644 index 0000000..3fb3e12 --- /dev/null +++ b/samples/dtrfs/sample.yaml @@ -0,0 +1,4 @@ +dtrfs_url: http://registry.detee.ltd/dtrfs-payments2025-01-23.cpio.gz +dtrfs_sha: 2e95d7969a0f2ae2ee6f37acd2789a032be1653e76ba93e607477c8b1cde42ed +kernel_url: http://registry.detee.ltd/vmlinuz-linux-6.12.10-arch1-1 +kernel_sha: f3a4a74b11c07efa0338c5741d44f13480727e8f2021364a64fcffe1706c6231 diff --git a/samples/new_app/new_app_deploy_config.yaml b/samples/new_app/new_app_deploy_config.yaml new file mode 100644 index 0000000..922a45b --- /dev/null +++ b/samples/new_app/new_app_deploy_config.yaml @@ -0,0 +1,11 @@ +node_pubkey: 3mWjE6FnKQ8f9WRjGHdj1Jtyewsri87GXQpqLWpwtjhr +package_url: https://registry.detee.ltd/sgx/packages/actix-env-info_public-package_2025-03-06_00-53-30.tar.gz +private_package: false +public_package_mr_enclave: [ 152, 174, 10, 201, 41, 45, 15, 100, 123, 209, 103, 181, 205, 70, 145, 159, 134, 130, 140, 238, 196, 87, 145, 63, 222, 1, 230, 140, 118, 26, 238, 86] +hours: 1 +node_unit_price: 200000 +resource: + vcpu: 2 + memory_mb: 512 + disk_mb: 1024 + port: [8080, 8081] diff --git a/samples/new_app/new_app_launch_config.yaml b/samples/new_app/new_app_launch_config.yaml new file mode 100644 index 0000000..4dba4b0 --- /dev/null +++ b/samples/new_app/new_app_launch_config.yaml @@ -0,0 +1,15 @@ +filesystems: + - path: /bin/actix-hello-world + content: !path "./samples/new_app/binaries/actix-test-app.bin" +environments: + - name: APP_NAME + value: actix-test-dtpm-foo-bar-koo + - name: PORT + value: 8080 +child_processes: + - path: /bin/actix-app-info + arguments: ["pg_1_arg_1", "pg_1_arg_2"] + restart: + max_retries: 2 + delay_seconds: 2 + policy: !OnNonZeroExit true diff --git a/samples/new_vm/advanced_os_config.yaml b/samples/new_vm/advanced_os_config.yaml new file mode 100644 index 0000000..2fc466a --- /dev/null +++ b/samples/new_vm/advanced_os_config.yaml @@ -0,0 +1,29 @@ +hostname: my-specific-vm-01 +price: 20000 +hours: 5 +location: + country: "GB" +# This will publish the port 22 +ipv4: !PublishPorts +# To publish extra ports, replace with: +# ipv4: !PublishPorts [ 80, 8080 ] +public_ipv6: false +vcpus: 2 +memory_mb: 2000 +disk_size_gb: 20 +# os_setup is an optional field that allows you to specify the operating system +# dtrfs is the DeTEE initramfs required to boot a VM. It also needs a kernel. +# The OS Template is normally a Linux distribution (without initrd and kernel) +# For more information, check out official documentation at https://detee.ltd +dtrfs: + name: whatever + vendor: My Company Ltd + dtrfs_url: http://registry.detee.ltd/detee-constantin-6.12.9-arch1-1.cpio.gz + dtrfs_sha: f1d4d818b5f403ec84b6f1f23cbca3d29ccad6db11941fd60fef1018d9116be4 + kernel_url: http://registry.detee.ltd/vmlinuz-linux-6.12.9-arch1-1 + kernel_sha: 8094abfd3a2a9dfdbc19b39d7e720eb43116b885abb36fc9431f0c18cbd5938e +distro: + name: ubuntu_2025-01-14 + vendor: ramil + template_url: http://registry.detee.ltd/ubuntu_os_template.fsa + template_sha: dab318f58c19d31181fc09a497d26408c06fb445913809075d7be74583172205 diff --git a/samples/new_vm/no_public_ips.yaml b/samples/new_vm/no_public_ips.yaml new file mode 100644 index 0000000..d4660f1 --- /dev/null +++ b/samples/new_vm/no_public_ips.yaml @@ -0,0 +1,13 @@ +hostname: my-vm-01 +hours: 5 +price: 20000 +location: + country: "GB" +# This will publish the port 22 +ipv4: !PublishPorts +# To publish extra ports, replace with: +# ipv4: !PublishPorts [ 80, 8080 ] +public_ipv6: false +vcpus: 2 +memory_mb: 2000 +disk_size_gb: 20 diff --git a/samples/new_vm/public_ipv4_and_ipv6.yaml b/samples/new_vm/public_ipv4_and_ipv6.yaml new file mode 100644 index 0000000..c86cb0c --- /dev/null +++ b/samples/new_vm/public_ipv4_and_ipv6.yaml @@ -0,0 +1,15 @@ +hostname: my-public-vm-01 +hours: 5 +price: 20000 +location: + # Note this is the service IP of the node, and not the IP of the VM. + # This is optional and can be used to pick the node that will offer the service. + node_ip: "173.234.17.2" +# This option will ask the node for a public IPv4. +# If you chose this option, you will receive the IP of the VM after deployment. +ipv4: !PublicIPv4 +# For IPv6, just specify true or false if you want a public IP +public_ipv6: true +vcpus: 2 +memory_mb: 2000 +disk_size_gb: 20 diff --git a/samples/new_vm/specific_city.yaml b/samples/new_vm/specific_city.yaml new file mode 100644 index 0000000..f102365 --- /dev/null +++ b/samples/new_vm/specific_city.yaml @@ -0,0 +1,14 @@ +hostname: my-bucharest-vm-01 +hours: 5 +price: 20000 +# All configurations under location are optional. +# You can specify: country, region, city +# To manually pick a server, specify node_ip +location: + # If a server is found, this will create a server in Bucharest + city: "Bucharest" +ipv4: !PublicIPv4 +public_ipv6: false +vcpus: 2 +memory_mb: 1000 +disk_size_gb: 20 diff --git a/scripts/detee-cli_injector.sh b/scripts/detee-cli_injector.sh new file mode 100755 index 0000000..9902b9f --- /dev/null +++ b/scripts/detee-cli_injector.sh @@ -0,0 +1,209 @@ +#!/bin/bash +set -e +cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" + +echo_blue() { + echo -e "\033[34m$1\033[0m" +} + +echo_yellow() { + echo -e "\033[0;33m$1\033[0m" +} + +echo_red() { + echo -e "\033[0;31m$1\033[0m" +} + +if [[ -z "${SERVER_ADDR}" ]]; then + echo_red "The SERVER_ADDR environment variable is needed." + exit 1 +fi + +if [[ -z "${MEASUREMENT}" ]]; then + echo_red "The MEASUREMENT environment variable is needed." + exit 1 +fi + +server="$SERVER_ADDR" +ssh_pubkey_dir="${HOME}/.detee/cli/vms/ssh" +cert_dir="${HOME}/.detee/cli/vms/certs" +snp_reports_dir="${HOME}/.detee/cli/vms/snpreports" +keyfile_dir="${HOME}/.detee/cli/vms/secrets" +keyfile="${keyfile_dir}/${server}" +mkdir -p "$ssh_pubkey_dir" +mkdir -p "$cert_dir" +mkdir -p "$snp_reports_dir" +mkdir -p "$keyfile_dir" +server_crt="${cert_dir}/${server}.crt" +server_report="${snp_reports_dir}/${server}-report.bin" + +mkdir -p "$(dirname "$keyfile")" +if [[ -f "$keyfile" ]]; then + echo_yellow "Found keyfile $keyfile" +else + echo_yellow "Creating keyfile $keyfile" + dd if=/dev/urandom "of=$keyfile" bs=32 count=4 +fi + +snpguest --help > /dev/null \ + || { + echo_yellow "Please install https://github.com/virtee/snpguest" + exit 3 +} + +try_countdown=20; +while [[ $try_countdown -gt 0 ]]; do + curl --max-time 1 -k "https://$server" > /dev/null 2>&1 && break + sleep 1 + ((try_countdown--)) +done + +openssl s_client -connect "$server" "$server_crt" +openssl x509 -in "$server_crt" -noout +crt_hash=$(openssl dgst -sha3-512 < "$server_crt" | awk '{ print $2}') + +echo_blue "The certificate hash is $crt_hash" + +echo_blue "Getting hash from attestation report..." +ip=$(echo "$server" | cut -d ":" -f1) +port=$(echo "$server" | cut -d ":" -f2) +echo "$server" | grep -F '[' | grep -F ']' && { + ip=$(echo "$server" | grep -oE '\[[a-f0-9\:]*\]') + port=$(echo "$server" | grep -oE '\]:[0-9]+' | cut -d ':' -f2) +} + +curl --cacert "$server_crt" \ + --resolve dtrfs-api:${port}:${ip} \ + "https://dtrfs-api:${port}/report" > "${server_report}.base64" + +cat "${server_report}.base64" | basenc --base64url -d > "$server_report" +rm "${server_report}.base64" + +report_crt_hash=$( snpguest display report "$server_report" \ + | grep "Report Data" -A 4 | tail -4 | tr '\n' ' ' | sed 's/\s//g') + +echo_blue "The hash in the report is $report_crt_hash" + +if [[ "$crt_hash" != "$report_crt_hash" ]]; then + echo The hash of the certificate does not match the hash from the report. Exiting. + exit 2 +fi + +echo_blue "The hash matches!" +echo_blue "Verifying AMD signature in attestation report..." + +chip_id_hash=$( snpguest display report "$server_report" \ + | grep "Chip ID:" -A 4 | tail -3 | tr '\n' ' ' | sed 's/\s//g' \ + | md5sum | awk '{ print $1 }') +microcode=$( snpguest display report "$server_report" | + grep "Launch TCB:" -A 6 | grep "Microcode:" | awk '{ print $2 }' ) +vcek_path="${cert_dir}/${chip_id_hash}-${microcode}.vcek.pem" +amd_certs_dir="${cert_dir}/amd_certs_${server}" +mkdir -p "$amd_certs_dir" + +# TODO: add support for Genoa, Bergamo, Siena and Turin +# Or at least for Genoa... +[[ -f "${cert_dir}/ask-milan.pem" ]] || { + snpguest fetch ca pem milan "$amd_certs_dir" --endorser vcek + mv "${amd_certs_dir}/ask.pem" "${cert_dir}/ask-milan.pem" + mv "${amd_certs_dir}/ark.pem" "${cert_dir}/ark-milan.pem" +} +ln -fs "${cert_dir}/ask-milan.pem" "${amd_certs_dir}/ask.pem" +ln -fs "${cert_dir}/ark-milan.pem" "${amd_certs_dir}/ark.pem" +[[ -f "${vcek_path}" ]] || { + snpguest fetch vcek pem milan "$amd_certs_dir" "$server_report" || { + # You are probably wondering what this weird shit is doing here. + # The AMD API for VCEK has throttling, and this scripts needs to run in parallel. + sleep 10 + [[ -f "${vcek_path}" ]] || { + snpguest fetch vcek pem milan "$amd_certs_dir" "$server_report" + } + } + mv "${amd_certs_dir}/vcek.pem" "${vcek_path}" +} +ln -fs "${vcek_path}" "${amd_certs_dir}/vcek.pem" +snpguest verify certs "$amd_certs_dir" +snpguest verify attestation "$amd_certs_dir" "$server_report" + +echo_yellow "The attestation got verified based on the CA from AMD for the Milan generation!" +echo_blue "Verifying if measurement is $MEASUREMENT..." + +guest_measurement=$( snpguest display report "$server_report" \ + | grep Measurement -A 3 | tail -3 | tr '\n' ' ' | sed 's/\s//g' ) + +echo_blue "The guests's measurement is $guest_measurement" + +if [[ "$guest_measurement" != "$MEASUREMENT" ]]; then + echo_red "The measurement of the server does not match." + echo_yellow "Please use this project to get your measurement: https://github.com/virtee/sev-snp-measure" + echo_yellow "After that, please sepcify the measurement ast the MEASUREMENT environment variable." + exit 2 +fi + +echo_yellow "The measurement matched!" + +echo_blue "Creating ed25519 signature..." +ed25519_signature=$(detee-cli account sign "$server_crt" || exit 1) + +if [[ -n "$DETEE_INSTALL_URL" ]] && [[ -n "$DETEE_INSTALL_URL" ]]; then + echo_blue "Injecting installation url: ${DETEE_INSTALL_URL}" + curl --fail-with-body \ + -H "ed25519-signature: ${ed25519_signature}" \ + --cacert "$server_crt" \ + --resolve "dtrfs-api:${port}:${ip}" \ + --data-urlencode "url=${DETEE_INSTALL_URL}" \ + -d "sha=${DETEE_INSTALL_SHA}" \ + -d "keyfile=$(cat "$keyfile" | basenc --base64url -w 0)" \ + "https://dtrfs-api:${port}/install" || exit 1 +else + echo + echo_blue "Injecting keyfile..." + curl --fail-with-body \ + -H "ed25519-signature: ${ed25519_signature}" \ + --cacert "$server_crt" \ + --resolve "dtrfs-api:${port}:${ip}" \ + -d "keyfile=$(cat "$keyfile" | basenc --base64url -w 0)" \ + "https://dtrfs-api:${port}/decrypt" || exit 1 +fi + +[[ -z "$SSH_KEY_FILE" ]] && SSH_KEY_FILE="$HOME/.ssh/id_ed25519.pub" +echo +echo_blue "Injecting SSH key from file ${SSH_KEY_FILE}" +curl --fail-with-body \ + -H "ed25519-signature: ${ed25519_signature}" \ + --cacert "$server_crt" \ + --resolve "dtrfs-api:${port}:${ip}" \ + --data-urlencode "ssh_key=$(cat ${SSH_KEY_FILE})" \ + "https://dtrfs-api:${port}/authorized_keys" || exit 1 + +echo +echo_blue "Downloading SSH keys from server" +curl --fail-with-body \ + -H "ed25519-signature: ${ed25519_signature}" \ + --cacert "$server_crt" \ + --resolve "dtrfs-api:${port}:${ip}" \ + "https://dtrfs-api:${port}/server_ssh_pubkeys" > "${ssh_pubkey_dir}/${server}" || { + echo_red "Could not grab SSH pubkeys from the server. This could be a MITM attack. Error:" + cat "${ssh_pubkey_dir}/${server}" + echo + echo_yellow "SSH at your own risk. IP: ${ip} Port: ${port} User: root" + curl -X POST --cacert "$server_crt" \ + -H "ed25519-signature: ${ed25519_signature}" \ + --resolve dtrfs-api:${port}:${ip} \ + "https://dtrfs-api:${port}/switch_root" 2>/dev/null + exit 1 + } +[[ "$port" != "22" ]] && ssh_keygen_ip="[$ip]:$port" || ssh_keygen_ip="$ip" +ssh-keygen -R "$ssh_keygen_ip" -f "${HOME}/.ssh/known_hosts2" || true +cat "${ssh_pubkey_dir}/${server}" | + awk '{ print $1 " " $2 }' | + xargs -I {} echo "$ssh_keygen_ip" {} >> "${HOME}/.ssh/known_hosts2" + +echo +echo_blue "Starting guest OS. Give it a few seconds and try to SSH into the guest." +# this command is supposed to fail; it kills the API in the initrd +curl -X POST --cacert "$server_crt" \ + -H "ed25519-signature: ${ed25519_signature}" \ + --resolve dtrfs-api:${port}:${ip} \ + "https://dtrfs-api:${port}/switch_root" 2>/dev/null || echo Success. diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..bb45301 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,96 @@ +#!/bin/bash +cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" +scriptdir="$(pwd)" + +echo_cyan() { + echo -e "\033[0;36m$1\033[0m" +} +echo_blue() { + echo -e "\033[0;34m$1\033[0m" +} +echo_yellow() { + echo -e "\033[0;33m$1\033[0m" +} +echo_red() { + echo -e "\033[0;31m$1\033[0m" +} + + +mkdir -p ${HOME}/.detee/bin +mkdir -p ${HOME}/.detee/tmp +mkdir -p ${HOME}/.detee/deps +mkdir -p ${HOME}/.detee/ + +echo_blue "Make sure you add ${HOME}/.detee/bin to your path!" + +install_sev-snp-measure() { + [[ -e "${HOME}/.detee/bin/sev-snp-measure.py" ]] && { + echo_yellow "${HOME}/.detee/bin/sev-snp-measure.py already found. Delete the file to reinstall." + return + } + cd ${HOME}/.detee/deps + git clone https://github.com/virtee/sev-snp-measure.git + cd sev-snp-measure + git pull + ln -s ../deps/sev-snp-measure/sev-snp-measure.py ${HOME}/.detee/bin/sev-snp-measure.py +} + +install_snpguest() { + [[ -f "${HOME}/.detee/bin/snpguest" ]] && { + echo_yellow "${HOME}/.detee/bin/snpguest already found. Delete the file to reinstall." + return + } + cd ${HOME}/.detee/tmp + git clone https://github.com/virtee/snpguest.git + cd snpguest + git pull + cargo build --release + cp target/release/snpguest ${HOME}/.detee/bin +} + +install_detee-cli() { + cd "${scriptdir}/.." + [[ -f "${HOME}/.detee/bin/detee-cli" ]] && { + echo_yellow "${HOME}/.detee/bin/detee-cli already found. Delete the file to reinstall." + return + } + cargo build --release + cp ./target/release/detee-cli ${HOME}/.detee/bin +} + +install_super-detee-cli() { + cd "${scriptdir}/.." + [[ -f "${HOME}/.detee/bin/super-detee-cli" ]] && { + echo_yellow "${HOME}/.detee/bin/super-detee-cli already found. Delete the file to reinstall." + return + } + cargo build --release --bin super-detee-cli + cp ./target/release/super-detee-cli ${HOME}/.detee/bin/ +} + +install_injector() { + cd "${scriptdir}" + [[ -f "${HOME}/.detee/bin/detee-cli_injector.sh" ]] && { + echo_yellow "${HOME}/.detee/bin/detee-cli_injector.sh already found. Delete the file to reinstall." + return + } + cp ./detee-cli_injector.sh ${HOME}/.detee/bin/ +} + +copy_vm_samples() { + cd $scriptdir + cp -r ../samples "${HOME}/.detee/" +} + +install_fish_completion() { + detee-cli completion fish > ${HOME}/.config/fish/completions/detee-cli.fish + super-detee-cli completion fish > ${HOME}/.config/fish/completions/super-detee-cli.fish +} + +install_sev-snp-measure +install_snpguest +install_detee-cli +install_super-detee-cli +install_injector +copy_vm_samples +install_fish_completion diff --git a/src/bin/detee-cli.rs b/src/bin/detee-cli.rs new file mode 100644 index 0000000..511565a --- /dev/null +++ b/src/bin/detee-cli.rs @@ -0,0 +1,756 @@ +use clap::{builder::PossibleValue, Arg, ArgMatches, Command}; +use clap_complete::{generate, Shell}; +use detee_cli::sgx::cli_handler::handle_app; +use detee_cli::*; +use snp; +use std::error::Error; +use std::io; + +const ABOUT: &str = r#"The DeTEE CLI allows you to manage and deploy applications and virtual machines. +All software runs within Trusted Execution Environments on a distributed network. +More information can be found at https://detee.ltd + +Feel free to browser applications bundles or VM disks available for immediate deployment."#; + +fn main() { + let log_level = match std::env::var("LOG_LEVEL") { + Ok(val) => match val.as_str() { + "DEBUG" => log::LevelFilter::Debug, + "INFO" => log::LevelFilter::Info, + _ => log::LevelFilter::Error, + }, + _ => log::LevelFilter::Warn, + }; + env_logger::builder().filter_level(log_level).format_timestamp(None).init(); + + let cmd = Command::new("detee-cli") + .version("0.0.1") + .author("https://detee.ltd") + .about(ABOUT) + .arg( + Arg::new("format") + .help("format output as JSON or YAML") + .long("format") + .default_value("human") + .value_parser(["human", "json", "yaml"]) + ) + .subcommand(Command::new("completion") + .about("generates shell completion scripts") + .arg( + Arg::new("shell") + .help("The shell to generate the script for") + .value_parser(["bash", "zsh", "fish"]) + .required(true), + ) + ) + .subcommand(Command::new("app") + .about("a lightweight service that run on Intel SGX") + .subcommand( + Command::new("package") + .about("package new app from x86_64-linux-musl binary") + .arg( + Arg::new("package-type") + .long("package-type") + .help("Enclave package type") + .long_help( + "Type of package, public or private. public packages are signed by DeTEE,".to_string() + + "Public packages are used wellknown mr_enclave for hratls connection into enclave." + ) + .default_value("public") + .value_parser(["public", "private"]) + ) + .arg( + Arg::new("files") + .help("List of binaries to package") + .long_help("List of x86_64 linux musl binaries to package") + .num_args(1..) + .required(true) + .value_name("FILE") + .value_delimiter(' ') + + )) + .subcommand( + Command::new("deploy") + .about("create new app from a YAML configuration file") + .arg( + Arg::new("yaml-path") + .long("from-yaml") + .help("allows extended config through App deploy config yaml") + .long_help("Allows extended configuration options, including:".to_owned() + + "\n- deploying to a specific node or to a specific city.") + .exclusive(true) + ) + .arg( + Arg::new("vcpus") + .long("vcpus") + .default_value("1") + .value_parser(clap::value_parser!(u32).range(1..64)) + .help("the number of vCPUs") + ) + .arg( + Arg::new("memory") + .long("memory") + .default_value("500") + .value_parser(clap::value_parser!(u32).range(200..2050)) + .help("memory in MB") + ) + .arg( + Arg::new("disk") + .long("disk") + .default_value("1000") + .value_parser(clap::value_parser!(u32).range(300..8000)) + .help("disk size in MB") + ) + .arg( + Arg::new("port") + .long("port") + .value_parser(clap::value_parser!(u32).range(0..65535)) + .action(clap::ArgAction::Append) + .help("Application exposing port") + .long_help("Port to expose on the application which mapped into the host's public IP's port") + ) + .arg( + Arg::new("package-url") + .long("package-url") + .help("Enclave package url") + .long_help( + "Package registry url, detee-cli app package ".to_string() + + "check detee-cli app package --help for more information" + ) + ) + .arg( + Arg::new("package-type") + .long("package-type") + .help("Enclave package type") + .long_help( + "Type of package, public or private. public packages are signed by DeTEE,".to_string() + + "Public packages are used wellknown mr_enclave for hratls connection into enclave." + ) + .default_value("public") + .value_parser(["public", "private"]) + ) + .arg( + Arg::new("name") + .long("name") + .help("app name") + ) + .arg( + Arg::new("hours") + .long("hours") + .short('h') + .help("for how many hours should the app run") + .default_value("1") + .value_parser(clap::value_parser!(u64).range(1..5000)) + .long_help("How long should the app run for so it locks up LP accordingly") + ) + .arg( + Arg::new("price") + .long("price") + .help("price per unit per minute; check docs") + .default_value("200000") + .value_parser(clap::value_parser!(u64).range(1..50000000)) + ) + // TODO: implement location + .arg( + Arg::new("location") + .help("deploy to a specific location") + .long("location") + .default_value("DE") + .value_parser([ + PossibleValue::new("DE").help("Frankfurt am Main, Hesse, Germany"), + // PossibleValue::new("GB").help("London, England, GB"), + // PossibleValue::new("Canada").help("Montréal or Vancouver"), + // PossibleValue::new("Montreal").help("Montréal, Quebec, CA"), + // PossibleValue::new("Vancouver").help("Vancouver, British Columbia, CA"), + // PossibleValue::new("California").help("San Jose, California, US"), + // PossibleValue::new("US").help("San Jose, California, US"), + // PossibleValue::new("France").help("Paris, Île-de-France, FR"), + // PossibleValue::new("Random").help("Just deploy somewhere..."), + ]), + ) + ) + .subcommand( + Command::new("delete") + .about("delete deployed app") + .arg( + Arg::new("uuid") + .help("supply the uuid of the App contract") + .required(true) + ) + ) + .subcommand( + Command::new("config") + .about("App launch config in a YAML file") + .long_about("The YAML configuration file for the environment variables, child processes, restart policy and arguments for the app") + .subcommand( + Command::new("validate") + .about("Validate a YAML configuration file") + .arg( + Arg::new("config") + .help("App config yaml file path to validate") + .long_help("Validate YAML configuration file for the app which you want to run in the enclave") + .required(true) + ) + ) + .subcommand( + Command::new("update") + .about("App launch config yaml file path to update in enclave") + .long_about("Update the YAML configuration file for the app which you want to run in the enclave") + .arg( + Arg::new("config") + .required(true), + ) + .arg( + Arg::new("uuid") + .help("supply the uuid of the App contract") + .required(true) + ) + ) + .subcommand( + Command::new("get") + .about("download current launch configuration file from enclave") + .arg( + Arg::new("path") + .help("path to save launch config") + .long_help("path to save current launch config from enclave as yaml") + .required(true), + ) + .arg( + Arg::new("uuid") + .help("supply the uuid of the App contract") + .required(true) + ) + ) + ) + .subcommand( + Command::new("list") + .about("list all deployed apps") + .long_about("List all the deployed apps in the enclave") + ) + ) + .subcommand(Command::new("vm") + .about("virtual machines that run on AMD SEV-SNP nodes") + .subcommand(Command::new("deploy").about("deploy a VM on the DeTEE network") + .arg( + Arg::new("yaml-path") + .long("from-yaml") + .help("allows extended config through yaml") + .long_help("Allows extended configuration options, including:".to_owned() + + "\n- deploying to a specific node or to a specific city." + + "\n- specifying the kernel and dtrfs version." + + "\n- configuring extra ports to publish for a VM no public IPv4" + + "\n- request a public IPv6 address for yout VM" + + "\nSamples can be found in ~/.detee/samples/new_vm/") + .exclusive(true) + ) + .arg( + Arg::new("location") + .help("deploy to a specific location") + .long("location") + .default_value("Vancouver") + .value_parser([ + PossibleValue::new("GB").help("London, England, GB"), + PossibleValue::new("Canada").help("Montréal or Vancouver"), + PossibleValue::new("Montreal").help("Montréal, Quebec, CA"), + PossibleValue::new("Vancouver").help("Vancouver, British Columbia, CA"), + PossibleValue::new("California").help("San Jose, California, US"), + PossibleValue::new("US").help("San Jose, California, US"), + PossibleValue::new("France").help("Paris, Île-de-France, FR"), + PossibleValue::new("Random").help("Just deploy somewhere..."), + ]), + ) + .arg( + Arg::new("vcpus") + .long("vcpus") + .default_value("1") + .value_parser(clap::value_parser!(u32).range(1..64)) + .help("the number of vCPUs") + ) + .arg( + Arg::new("memory") + .long("memory") + .default_value("1000") + .value_parser(clap::value_parser!(u32).range(800..123000)) + .help("memory in MB") + ) + .arg( + Arg::new("disk") + .long("disk") + .default_value("10") + .value_parser(clap::value_parser!(u32).range(5..500)) + .help("disk size in GB") + ) + .arg( + Arg::new("distribution") + .long("distro") + .help("GNU/Linux distribution") + .default_value("arch") + .value_parser(["arch", "ubuntu", "fedora"]) + ) + .arg( + Arg::new("hours") + .long("hours") + .help("for how many hours should the VM run") + .default_value("1") + .value_parser(clap::value_parser!(u32).range(1..5000)) + ) + .arg( + Arg::new("price") + .long("price") + .help("price per unit per minute; check docs") + .default_value("20000") + .value_parser(clap::value_parser!(u64).range(1..50000000)) + ) + .arg( + Arg::new("hostname") + .long("hostname") + .help("hostname of you VM and OS") + ) + .arg( + Arg::new("public-ip") + .long("public-ip") + .help("get a public IPv4 address for this VM") + .action(clap::ArgAction::SetTrue) + ) + ) + .subcommand(Command::new("inspect").about("list all available information about a VM") + .arg( + Arg::new("uuid") + .help("supply the uuid of the VM contract") + .required(true) + ) + .arg( + Arg::new("show-node") + .long("show-node") + .help("inspect the node that is runnnig this VM") + .action(clap::ArgAction::SetTrue) + ) + ) + .subcommand(Command::new("ssh").about("connect to the VM using SSH") + .arg( + Arg::new("uuid") + .help("supply the uuid of the VM contract") + .required(true) + ) + .arg( + Arg::new("just-print") + .long("just-print") + .help("print the command instead of connecting") + .action(clap::ArgAction::SetTrue) + ) + ) + .subcommand(Command::new("list").about("list all existing VM contracts") + .arg( + Arg::new("as-operator") + .long("as-operator") + .help("list VMs running on your SNP nodes") + .action(clap::ArgAction::SetTrue) + ) + ) + .subcommand(Command::new("delete").about("delete a VM from the DeTEE network") + .arg( + Arg::new("uuid") + .help("uuid of the VM that you wish to delete") + .required(true) + ) + ) + .subcommand(Command::new("update") + .about("update the hardware or the lifetime of a VM") + .long_about("Allows you to update the hardware or the lifetime".to_string() + + "\nAny hardware modifiations will restart the VM." + + "\nChanging the lifetime of a VM will not restart." + + "\nIf changing the lifetime to a higher value, LP will locked accordingly.") + .arg( + Arg::new("uuid") + .help("supply the uuid of the VM you wish to upgrade") + .required(true) + ) + .arg( + Arg::new("vcpus") + .long("vcpus") + .default_value("0") + .value_parser(clap::value_parser!(u32).range(0..100)) + .help("modify the number of vCPUs") + ) + .arg( + Arg::new("memory") + .long("memory") + .default_value("0") + .value_parser(clap::value_parser!(u32).range(0..115000)) + .help("modify the MB of memory reserved") + ) + .arg( + Arg::new("disk") + .long("disk") + .default_value("0") + .value_parser(clap::value_parser!(u32).range(0..500)) + .help("increase the size of the disk in GB") + ) + .arg( + Arg::new("hours") + .long("hours") + .help("delete vm after the time expires") + .default_value("0") + .value_parser(clap::value_parser!(u32).range(0..5000)) + ) + .arg( + Arg::new("dtrfs") + .long("dtrfs") + .help(r#""update kernel to "latest" or custom""#) + .long_help("Reboot the VM and upgrade dtrfs together with the kernel.\n".to_owned() + + r#"Specify "latest" to simply upgrade to the latest kernel"# + + "\nAlternatively, you can specify full path to DTRFS config." + + "\nSamples can be found in ~/.detee/samples/dtrfs/") + .default_value("") + ) + ) + .subcommand(Command::new("distro") + .about("GNU/Linux distributions for Virtual Machines") + .subcommand( + Command::new("search").about("search public registry for distributions"), + ) + ) + .subcommand(Command::new("dtrfs") + .about("DeTEE initramfs archives required to boot a VM") + .subcommand(Command::new("list").about("list locally stored dtrfs")) + ) + ) + .subcommand(Command::new("vm-node") + .about("info about AMD SEV-SNP servers registerd to DeTEE") + .subcommand(Command::new("search").about("search nodes based on filters")) + .subcommand(Command::new("inspect").about("get detailed information about a node") + .arg( + Arg::new("ip") + .long("ip") + .help("main IP of the node your want to inspect") + .required(true) + ) + ) + .subcommand(Command::new("report").about("report a node for poor performance") + .arg( + Arg::new("pubkey") + .long("pubkey") + .help("public key of the node you are reporting") + .required(true) + ) + .arg( + Arg::new("contract") + .long("contract") + .help("UUID of the active contract with this node") + .required(true) + ) + .arg( + Arg::new("reason") + .long("reason") + .help("detail the performance issue you experienced") + ) + ) + ) + .subcommand(Command::new("operator") + .about("node operators host nodes on the DeTEE network") + .subcommand(Command::new("list").about("list operators currently offering servers")) + .subcommand(Command::new("register").about("register as a node operator to boost rewards") + .arg( + Arg::new("escrow") + .long("escrow") + .help("At least 5000 LP is required as escrow") + .long_help("Escrow is used by node operators to guarantee quality.".to_owned() + + "\nBefore adding escrow, make sure you booted a node under your account." + + "\nWhen all your nodes got decomissioned, your escrow gets automatically returned.") + .default_value("5000") + .value_parser(clap::value_parser!(u64).range(5000..100000)) + ) + .arg( + Arg::new("email") + .long("email") + .help("email address must be supplied as contact point") + .required(true) + ) + ) + .subcommand(Command::new("inspect").about("inspect yourself or other opertors") + .arg( + Arg::new("wallet") + .long("wallet") + .help("specify wallet address of the operator") + ) + ) + .subcommand(Command::new("kick").about("terminate a contract (refund up to 1 week)") + .arg( + Arg::new("contract") + .long("contract") + .help("unique ID of the contract you wish to kick") + ) + .arg( + Arg::new("reason") + .long("reason") + .help("optionally mention why you made this decission") + ) + ) + .subcommand(Command::new("ban-user").about("ban a user from your services") + .arg( + Arg::new("wallet") + .long("wallet") + .help("wallet address of the user you are banning") + ) + ) + .subcommand(Command::new("decom").about("announce that a node is going offline") + .arg( + Arg::new("pubkey") + .long("pubkey") + .help("public key of the node you are decomissioning") + .required(true) + ) + .arg( + Arg::new("days") + .long("days") + .help("days left till the node will go offline") + .value_parser(clap::value_parser!(u64).range(7..365)) + ) + ) + ) + .subcommand(Command::new("packager") + .about("show a list of software packagers") + ) + .subcommand(Command::new("account") + .about("account and security data for this CLI") + .subcommand(Command::new("show").about("show account data associated with this CLI"), + ) + .subcommand(Command::new("sign").about("sign a file using your DeTEE key") + .arg( + Arg::new("path") + .help("supply path to your public SSH key") + .required(true) + ) + ) + .subcommand(Command::new("ssh-pubkey-path").about("define the SSH pubkey that you want to use") + .arg( + Arg::new("path") + .help("supply path to your public SSH key") + .required(true) + ) + ) + .subcommand(Command::new("brain-url").about("define the URL of the brain you work with") + .arg( + Arg::new("url") + .help("supply the URL of the brain you want to connect to") + .required(true) + ) + ) + ); + + let matches = cmd.clone().get_matches(); + match matches.get_one::("format").unwrap().as_str() { + "json" => std::env::set_var("FORMAT", "JSON"), + "yaml" => std::env::set_var("FORMAT", "YAML"), + _ => (), + } + match matches.subcommand() { + Some(("completion", subcom_args)) => handle_completion(subcom_args, cmd), + Some(("app", subcom_args)) => handle_app(subcom_args), + Some(("vm", subcom_args)) => handle_vm(subcom_args), + Some(("vm-node", subcom_args)) => handle_vm_nodes(subcom_args), + Some(("operator", subcom_args)) => handle_operators(subcom_args), + Some(("packager", subcom_args)) => cli_print(handle_packagers(subcom_args)), + Some(("account", subcom_args)) => handle_account(subcom_args), + _ => { + println!("No valid subcommand provided. Use --help for more information."); + std::process::exit(0); + } + } +} + +fn disable_help_for_all_subcommands(mut cmd: Command) -> Command { + cmd = cmd.disable_help_subcommand(true); + let subcommands: Vec<_> = cmd + .get_subcommands_mut() + .map(|sub| disable_help_for_all_subcommands(sub.clone())) + .collect(); + for sub in subcommands { + cmd = cmd.subcommand(sub); + } + cmd +} + +fn handle_completion(matches: &ArgMatches, cmd: Command) { + let mut cmd = disable_help_for_all_subcommands(cmd); + if let Some(shell) = matches.get_one::("shell") { + let shell: Shell = shell.parse().expect("Invalid shell type"); + generate(shell, &mut cmd, "detee-cli", &mut io::stdout()); + if shell.to_string() == "fish" { + println!("complete -c detee-cli -n '__fish_seen_subcommand_from vm delete' -a '(cat ~/.detee/cli/vms/uuid_list)' -f"); + println!("complete -c detee-cli -n '__fish_seen_subcommand_from vm update' -a '(cat ~/.detee/cli/vms/uuid_list)' -f"); + println!("complete -c detee-cli -n '__fish_seen_subcommand_from vm inspect' -a '(cat ~/.detee/cli/vms/uuid_list)' -f"); + println!("complete -c detee-cli -n '__fish_seen_subcommand_from vm ssh' -a '(cat ~/.detee/cli/vms/uuid_list)' -f"); + } + } +} + +fn handle_vm(matches: &ArgMatches) { + match matches.subcommand() { + Some(("deploy", args)) => cli_print(handle_vm_deploy(args)), + Some(("ssh", args)) => cli_print(handle_vm_ssh(args)), + Some(("list", args)) => cli_print(handle_vm_list(args)), + Some(("inspect", inspect_args)) => { + let uuid: String = inspect_args.get_one::("uuid").unwrap().clone(); + if *inspect_args.get_one::("show-node").unwrap() { + cli_print(snp::get_node_by_contract(&uuid).map_err(Into::into)); + } else { + cli_print(snp::get_one_contract(&uuid).map_err(Into::into)); + } + } + Some(("update", args)) => cli_print(handle_vm_update(args)), + Some(("delete", args)) => cli_print(handle_vm_delete(args)), + Some(("dtrfs", subcom_args)) => cli_print(handle_vm_dtrfs(subcom_args)), + Some(("distro", _)) => cli_print(Ok(crate::snp::Distro::get_template_list())), + _ => println!("No valid VM subcommand provided."), + } +} + +fn handle_vm_deploy(matches: &ArgMatches) -> Result> { + if let Some(path) = matches.get_one::("yaml-path") { + return Ok(snp::deploy::Request::load_from_yaml(path)?); + } + let hostname = match matches.get_one::("hostname") { + Some(hostname) => hostname.to_string(), + None => name_generator::random_vm_name(), + }; + let location = matches.get_one::("location").unwrap().as_str(); + let ipv4: crate::snp::deploy::IPv4Config = match matches.get_one::("public-ip").unwrap() { + true => crate::snp::deploy::IPv4Config::PublicIPv4, + false => crate::snp::deploy::IPv4Config::PublishPorts(Vec::new()), + }; + let distro = + crate::snp::Distro::from_string(matches.get_one::("distribution").unwrap()); + let vm_config = snp::deploy::Request { + hostname, + location: location.into(), + ipv4, + public_ipv6: false, + vcpus: *matches.get_one::("vcpus").unwrap(), + memory_mb: *matches.get_one::("memory").unwrap(), + disk_size_gb: *matches.get_one::("disk").unwrap(), + dtrfs: None, + hours: *matches.get_one::("hours").unwrap(), + price: *matches.get_one::("price").unwrap(), + distro: Some(distro), + }; + Ok(vm_config.deploy()?) +} + +fn handle_vm_update(update_vm_args: &ArgMatches) -> Result> { + let uuid = update_vm_args.get_one::("uuid").unwrap().clone(); + let memory = *update_vm_args.get_one::("memory").unwrap(); + if memory > 0 && memory < 800 { + log::error!("At least 800MB of memory must be assgined to the VM"); + return Ok(SimpleOutput::from("")); + } + snp::update::Request::process_request( + &uuid, + *update_vm_args.get_one::("vcpus").unwrap(), + memory, + *update_vm_args.get_one::("disk").unwrap(), + &update_vm_args.get_one::("dtrfs").unwrap().to_string(), + )?; + let hours = *update_vm_args.get_one::("hours").unwrap(); + if hours != 0 { + snp::update::expand_vm_hours(&uuid, hours)?; + } + Ok(SimpleOutput::from("VM successfully updated.")) +} + +fn handle_vm_list(update_vm_args: &ArgMatches) -> Result, Box> { + Ok(snp::list_contracts(*update_vm_args.get_one::("as-operator").unwrap())?) +} + +fn handle_vm_ssh(ssh_args: &ArgMatches) -> Result> { + let uuid: String = ssh_args.get_one::("uuid").unwrap().clone(); + Ok(snp::ssh(&uuid, *ssh_args.get_one::("just-print").unwrap())?) +} + +fn handle_vm_delete(delete_args: &ArgMatches) -> Result> { + let uuid: String = delete_args.get_one::("uuid").unwrap().clone(); + snp::delete_contract(&uuid)?; + Ok(SimpleOutput::from("VM successfully deleted.")) +} + +fn handle_vm_dtrfs(_matches: &ArgMatches) -> Result, Box> { + Ok(crate::snp::Dtrfs::print_dtrfs_list()) +} + +fn handle_vm_nodes(matches: &ArgMatches) { + match matches.subcommand() { + Some(("search", _)) => cli_print(snp::print_nodes().map_err(Into::into)), + Some(("inspect", path_subcommand)) => { + let ip: String = path_subcommand.get_one::("ip").unwrap().clone(); + cli_print(snp::inspect_node(ip).map_err(Into::into)); + } + Some(("report", path_subcommand)) => { + let node_pubkey: String = path_subcommand.get_one::("pubkey").unwrap().clone(); + let contract_uuid: String = + path_subcommand.get_one::("contract").unwrap().clone(); + let reason: String = path_subcommand.get_one::("reason").unwrap().clone(); + cli_print(snp::report_node(node_pubkey, contract_uuid, reason).map_err(Into::into)) + } + _ => { + println!("Available commands are search and report. Use --help for more information.") + } + } +} + +fn handle_account(matches: &ArgMatches) { + match matches.subcommand() { + Some(("show", _)) => cli_print(Ok(config::Config::get_account_data())), + Some(("sign", path_subcommand)) => { + let path: String = path_subcommand.get_one::("path").unwrap().clone(); + config::Config::init_config().sign_file(&path); + } + Some(("ssh-pubkey-path", path_subcommand)) => { + let path: String = path_subcommand.get_one::("path").unwrap().clone(); + config::Config::set_ssh_pubkey_path(&path); + } + Some(("brain-url", path_subcommand)) => { + let url: String = path_subcommand.get_one::("url").unwrap().clone(); + config::Config::set_brain_url(&url); + } + _ => cli_print(Ok(config::Config::get_account_data())), + } +} + +fn handle_operators(matches: &ArgMatches) { + match matches.subcommand() { + Some(("list", _)) => { + cli_print(crate::operators::print_operators().map_err(Into::into)); + } + Some(("register", subcom_args)) => { + let escrow: u64 = *subcom_args.get_one::("escrow").unwrap(); + let email: String = subcom_args.get_one::("email").unwrap().clone(); + cli_print(crate::operators::register(escrow, email).map_err(Into::into)); + } + Some(("inspect", inspect_args)) => { + let wallet = match inspect_args.get_one::("wallet") { + Some(wallet) => wallet.to_string(), + None => config::Config::get_detee_wallet().unwrap_or("".to_string()), + }; + cli_print(crate::operators::inspect_operator(wallet).map_err(Into::into)); + } + Some(("kick", subcom_args)) => { + let uuid: String = subcom_args.get_one::("contract").unwrap().clone(); + let reason: String = subcom_args.get_one::("reason").unwrap().clone(); + cli_print(crate::operators::kick(uuid, reason).map_err(Into::into)); + } + Some(("ban-user", subcom_args)) => { + let user_wallet: String = subcom_args.get_one::("wallet").unwrap().clone(); + cli_print(crate::operators::ban(user_wallet).map_err(Into::into)); + } + Some(("decom", _)) => { + todo!("Currently decomissioning is not supported. Will be "); + } + _ => println!("To get more information about operators, use: detee-cli operator --help"), + } +} + +fn handle_packagers( + _matches: &ArgMatches, +) -> Result, Box> { + Ok(crate::packagers::get_packagers()) +} diff --git a/src/bin/super-detee-cli.rs b/src/bin/super-detee-cli.rs new file mode 100644 index 0000000..5017c04 --- /dev/null +++ b/src/bin/super-detee-cli.rs @@ -0,0 +1,154 @@ +use clap::{Arg, ArgMatches, Command}; +use clap_complete::{generate, Shell}; +use detee_cli::config::Config; +use detee_cli::*; +use std::io; + +const ABOUT: &str = r#"The DeTEE Admin CLI got created for the testnet. +It allows you to: + - airdrop tokens to other accounts + - list all accounts + - list all contracts + +The admin pubkeys are hardcoded in the brain."#; + +fn main() { + let log_level = match std::env::var("LOG_LEVEL") { + Ok(val) => match val.as_str() { + "DEBUG" => log::LevelFilter::Debug, + "INFO" => log::LevelFilter::Info, + _ => log::LevelFilter::Error, + }, + _ => log::LevelFilter::Warn, + }; + env_logger::builder().filter_level(log_level).format_timestamp(None).init(); + + let cmd = Command::new("super-detee-cli") + .version("0.0.1") + .author("https://detee.ltd") + .about(ABOUT) + .subcommand( + Command::new("completion").about("generates shell completion scripts").arg( + Arg::new("shell") + .help("The shell to generate the script for") + .value_parser(["bash", "zsh", "fish"]) + .required(true), + ), + ) + .subcommand(Command::new("contract").about("list all vm contracts")) + .subcommand( + Command::new("airdrop") + .about("give an airdrop to a user") + .arg(Arg::new("wallet").help("pubkey of the wallet").long("wallet").required(true)) + .arg( + Arg::new("amount") + .help("the amount of token you want to airdrop") + .long("amount") + .value_parser(clap::value_parser!(u64).range(0..1_000_000)) + .required(true), + ), + ) + .subcommand( + Command::new("slash") + .about("forcefully slash a user") + .arg( + Arg::new("wallet") + .help("pubkey of the wallet you are slashing") + .long("wallet") + .required(true), + ) + .arg( + Arg::new("amount") + .help("the amount of tokens you are slashing") + .long("amount") + .value_parser(clap::value_parser!(u64).range(0..1_000_000)) + .required(true), + ), + ) + .subcommand( + Command::new("account") + .about("account and security data for this CLI") + .subcommand(Command::new("list").about("list all accounts that exist in the brain")) + .subcommand( + Command::new("show").about("show account data associated with this CLI"), + ) + .subcommand( + Command::new("brain-url") + .about("define the URL of the brain you work with") + .arg( + Arg::new("url") + .help("supply the URL of the brain you want to connect to") + .required(true), + ), + ), + ); + + let matches = cmd.clone().get_matches(); + + match matches.subcommand() { + Some(("completion", subcom_args)) => handle_completion(subcom_args, cmd), + Some(("contract", subcom_args)) => handle_contracts(subcom_args), + Some(("airdrop", subcom_args)) => handle_airdrop(subcom_args), + Some(("slash", subcom_args)) => handle_slash(subcom_args), + Some(("account", subcom_args)) => handle_account(subcom_args), + _ => println!("No valid subcommand provided. Use --help for more information."), + } +} + +fn handle_completion(matches: &ArgMatches, mut cmd: Command) { + if let Some(shell) = matches.get_one::("shell") { + let shell: Shell = shell.parse().expect("Invalid shell type"); + generate(shell, &mut cmd, "super-detee-cli", &mut io::stdout()); + } +} + +fn handle_account(matches: &ArgMatches) { + match matches.subcommand() { + Some(("show", _)) => cli_print(Ok(config::Config::get_account_data())), + Some(("list", _)) => match snp::grpc::block_on(snp::grpc::admin_list_accounts()) { + Ok(accounts) => { + for a in accounts { + println!("{} {} {}", a.pubkey, a.balance, a.tmp_locked); + } + } + Err(e) => println!("Could not get contracts due to error: {e:?}"), + }, + Some(("brain-url", path_subcommand)) => { + let url: String = path_subcommand.get_one::("url").unwrap().clone(); + Config::set_brain_url(&url); + } + _ => cli_print(Ok(config::Config::get_account_data())), + } +} + +fn handle_contracts(_matches: &ArgMatches) { + match snp::grpc::block_on(snp::grpc::admin_list_contracts()) { + Ok(contracts) => { + for c in contracts { + println!( + "{} {} {} {} {}", + c.admin_pubkey, c.created_at, c.hostname, c.nano_per_minute, c.locked_nano + ) + } + } + Err(e) => println!("Could not get contracts due to error: {e:?}"), + } +} + +fn handle_airdrop(matches: &ArgMatches) { + let wallet: String = matches.get_one::("wallet").unwrap().clone(); + let tokens = *matches.get_one::("amount").unwrap(); + match snp::grpc::block_on(snp::grpc::admin_airdrop(wallet, tokens)) { + Ok(()) => println!("Success."), + Err(e) => println!("Could not give airdrop due to error: {e:?}"), + } +} + +fn handle_slash(matches: &ArgMatches) { + let wallet: String = matches.get_one::("wallet").unwrap().clone(); + let tokens = *matches.get_one::("amount").unwrap(); + match snp::grpc::block_on(snp::grpc::admin_slash(wallet, tokens)) { + Ok(()) => println!("Success."), + Err(e) => println!("Could not slash wallet due to error: {e:?}"), + } +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..5bbca43 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,442 @@ +use ed25519_dalek::SigningKey; +use log::{debug, info, warn}; +use openssl::bn::BigNum; +use openssl::hash::{Hasher, MessageDigest}; +use openssl::pkey::{PKey, Private}; +use openssl::rsa::Rsa; +use serde::{Deserialize, Serialize}; +use std::{fs::File, io::Write, path::Path}; + +#[derive(Serialize, Default)] +pub struct AccountData { + path: String, + brain_url: String, + ssh_pubkey: String, + account_balance: f64, + locked_funds: f64, + wallet_address: String, + wallet_path: String, +} + +impl super::HumanOutput for AccountData { + fn human_cli_print(&self) { + if !self.brain_url.is_empty() { + println!("The brain URL is: {}", self.brain_url); + } + if !self.ssh_pubkey.is_empty() { + println!("The SSH key used for VMs is: {}", self.ssh_pubkey); + } + if !self.wallet_path.is_empty() { + println!("The address of your DeTEE wallet is {}", self.wallet_address); + println!("The balance of your account is {} LP", self.account_balance); + if self.locked_funds != 0.0 { + println!( + "WARNING! {} LP is temporary locked, waiting for a Contract.", + self.locked_funds + ); + } + if self.account_balance == 0.0 { + println!("Hop on discord to get an airdrop: https://discord.gg/DcfYczAMtD \n") + } + } + } +} + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct Config { + ssh_key_path: String, + default_dtrfs: String, + default_kernel: String, + brain_url: String, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Failed to get access to disk: {0}")] + DiskAccess(#[from] std::io::Error), + #[error("Parsing of the yaml config file failed: {0}")] + YamlFormat(#[from] serde_yaml::Error), + #[error("The private key of the DeTEE account got corrupted.")] + CorruptedKey, + #[error("Failed to generate key using openssl: {0}")] + Openssl(String), + #[error{"Failed to retrive/download the artefact"}] + ArtefactError, + #[error{"SSH key not defined. Run `detee-cli account` for more info."}] + SshKeyNoDefined, +} + +impl Config { + fn save_to_disk(&self) -> Result<(), Error> { + let path = Self::path()?; + debug!("Writing config file to disk at {}", path); + let mut file = File::create(path)?; + file.write_all(serde_yaml::to_string(self)?.as_bytes())?; + Ok(()) + } + + pub fn home_dir() -> String { + match std::env::var("HOME") { + Ok(path) => path, + Err(_) => { + // TODO: considering lowering log priority from warn to debug. + warn!( + "Could not find HOME env variable. Config will get saved to /opt/.detee/cli/" + ); + "/opt".to_string() + } + } + } + + pub fn logs_dir() -> Result { + let dir = Self::home_dir() + ("/.detee/cli/logs"); + if !Path::new(&dir).exists() { + warn!("Could not config dir. Creating {dir}"); + std::fs::create_dir_all(dir.clone())?; + } + Ok(dir) + } + + pub fn verify_and_install_artefacts(file_hash: &str) -> Result<(), Error> { + use std::fs; + let artefacts_dir = Config::artefacts_dir()? + "/"; + let file_path = Path::new(&artefacts_dir).join(&file_hash); + + if file_path.exists() { + log::debug!("Artefact '{}' already exists", file_hash); + return Ok(()); + } + log::debug!("Artefact '{}' not found. Proceeding to download...", file_hash); + let url = format!("https://registry.detee.ltd/{}", file_hash); + + let response = match reqwest::blocking::get(&url) { + Ok(resp) => resp, + Err(err) => { + log::error!("Failed to download artifact: {}", err); + return Err(Error::ArtefactError); + } + }; + + if !response.status().is_success() { + log::error!("Failed to download artifact: server responded with {}", response.status()); + return Err(Error::ArtefactError); + } + + let content = match response.bytes() { + Ok(bytes) => bytes, + Err(err) => { + log::error!("Error reading artifact data from response: {}", err); + return Err(Error::ArtefactError); + } + }; + + if let Err(e) = fs::write(&file_path, &content) { + log::error!("Error writing artifact to file: {}", e); + return Err(Error::ArtefactError); + } + + log::debug!("Downloaded atrifact {}", file_hash); + Ok(()) + } + + pub fn artefacts_dir() -> Result { + let dir = Self::home_dir() + "/.detee/artefacts"; + if !Path::new(&dir).exists() { + info!("Creating {dir}"); + warn!("Since {dir} does not exist, also assume meaurement will fail cause you need the artefacts."); + std::fs::create_dir_all(dir.clone())?; + } + Ok(dir) + } + + pub fn vm_uuid_list_path() -> Result { + let dir = Self::home_dir() + ("/.detee/cli/vms"); + if !Path::new(&dir).exists() { + std::fs::create_dir_all(dir.clone())?; + } + Ok(String::from(dir + "/uuid_list")) + } + + pub fn path_dir() -> Result { + let dir = Self::home_dir() + ("/.detee/cli"); + if !Path::new(&dir).exists() { + warn!("Could not config dir. Creating {dir}"); + std::fs::create_dir_all(dir.clone())?; + } + Ok(dir) + } + + fn path() -> Result { + let config_path = Self::path_dir()? + ("/cli-config.yaml"); + Ok(config_path) + } + + fn detee_wallet_key_path() -> Result { + let config_path = Self::path_dir()? + ("/secret_detee_wallet_key"); + Ok(config_path) + } + + fn load_config_from_file() -> Result { + Ok(serde_yaml::from_str(&std::fs::read_to_string(Self::path()?)?)?) + } + + pub fn init_config() -> Self { + // TODO: create if it does not exist + match Self::load_config_from_file() { + Ok(config) => config, + Err(e) => { + debug!("Could not load config due to error: {e}"); + eprintln!("Config file not found. Creating new config file!"); + let config = Self::default(); + if let Err(e) = config.save_to_disk() { + log::error!("Could not save config to disk: {e}"); + panic!("Could not initialize config."); + }; + config + } + } + } + + fn create_wallet_key() -> Result<(), Error> { + use rand::rngs::OsRng; + let mut csprng = OsRng; + + let key_path = Self::detee_wallet_key_path()?; + if Path::new(&key_path).exists() { + log::debug!("Found SNP admin key at {key_path}"); + return Ok(()); + } + let account_key = SigningKey::generate(&mut csprng); + + let mut account_file = File::create(key_path)?; + account_file.write_all(bs58::encode(account_key.to_bytes()).into_string().as_bytes())?; + + Ok(()) + } + + fn load_wallet_key() -> Result { + Self::create_wallet_key()?; + Ok(SigningKey::from_bytes( + &bs58::decode(std::fs::read_to_string(Self::detee_wallet_key_path()?)?.trim()) + .into_vec() + .map_err(|_| Error::CorruptedKey)? + .try_into() + .map_err(|_| Error::CorruptedKey)?, + )) + } + + pub fn get_detee_wallet() -> Result { + Ok(bs58::encode(Self::load_wallet_key()?.verifying_key().to_bytes()).into_string()) + } + + pub fn try_sign_message(message: &str) -> Result { + use ed25519_dalek::Signer; + let key = Self::load_wallet_key()?; + Ok(bs58::encode(key.sign(message.as_bytes()).to_bytes()).into_string()) + } + + fn try_sign_file(&self, path: &str) -> Result { + use ed25519_dalek::Signer; + let file_bytes = std::fs::read(path)?; + let key = Self::load_wallet_key()?; + Ok(bs58::encode(key.sign(&file_bytes).to_bytes()).into_string()) + } + + pub fn sign_file(&self, path: &str) { + match self.try_sign_file(path) { + Ok(s) => println!("{s}"), + Err(e) => { + log::error!("coult not sign file due to error: {e}"); + std::process::exit(1); + } + } + } + + pub fn set_ssh_pubkey_path(ssh_pubkey_path: &str) { + let mut config = Self::init_config(); + match std::fs::read_to_string(ssh_pubkey_path) { + Ok(content) => { + info!("Found the file {ssh_pubkey_path}"); + info!("The file contains the following data:\n{content}"); + info!("This will be used as your public key."); + } + Err(e) => { + log::error!("Opening {ssh_pubkey_path} returned an error: {e:?}"); + warn!("Cancelling operation!"); + return; + } + }; + config.ssh_key_path = ssh_pubkey_path.to_string(); + if let Err(e) = config.save_to_disk() { + log::error!("Could not save new SSH key path to config: {e}"); + } + } + + pub fn get_ssh_pubkey(&self) -> Result { + if self.ssh_key_path.is_empty() { + return Err(Error::SshKeyNoDefined); + } + Ok(self.ssh_key_path.clone()) + } + + pub fn get_brain_url() -> String { + Self::init_config().brain_url + } + + pub fn set_brain_url(brain_url: &str) { + let mut config = Self::init_config(); + info!("Setting brain URL to {brain_url}"); + config.brain_url = brain_url.to_string(); + if let Err(e) = config.save_to_disk() { + log::error!("Could not save new brain URL to config: {e}"); + } + } + + pub fn get_account_data() -> AccountData { + let mut account_data = AccountData { ..Default::default() }; + let config = Self::init_config(); + match Self::path() { + Ok(path) => account_data.path = path, + Err(e) => { + println!("Could not read config due to error: {e}"); + std::process::exit(1); + } + } + if config.brain_url.is_empty() { + log::error!("The brain URL is not set! To configure it, run:"); + eprintln!(r#" detee-cli account brain-url "http://[::1]:31337/""#); + } else { + account_data.brain_url = config.brain_url; + } + if config.ssh_key_path.is_empty() { + log::error!("SSH public key path not set! To configure it, run:"); + eprintln!(" detee-cli account ssh-pubkey-path /home/your_user/.ssh/id_ed25519.pub"); + } else { + account_data.ssh_pubkey = config.ssh_key_path; + } + match Self::get_detee_wallet() { + Ok(key) => { + account_data.wallet_address = key.to_string(); + match crate::snp::grpc::block_on(crate::snp::grpc::get_balance(&key)) { + Ok(account) => { + account_data.account_balance = account.balance as f64 / 1_000_000_000.0; + account_data.locked_funds = account.tmp_locked as f64 / 1_000_000_000.0; + } + Err(e) => log::error!("Could not get account balance due to error: {e}"), + } + } + Err(e) => log::error!("Please report this bug. Could not load admin key: {e}"), + } + match Self::detee_wallet_key_path() { + Ok(path) => account_data.wallet_path = path, + Err(_) => log::error!("This error should never happen. Please report this bug."), + } + account_data + } +} + +impl Config { + pub fn get_hratls_private_key() -> (String, String) { + ( + std::fs::read_to_string(Self::hratls_private_key_path()).unwrap_or_else(|_| { + Self::create_hratls_key().expect("Failed to create HRATLS key") + }), + Self::hratls_private_key_path(), + ) + } + + fn create_hratls_key() -> Result> { + let private_key_path = Self::hratls_private_key_path(); + if Path::new(&private_key_path).exists() { + log::debug!("Found HRaTLS private key at {private_key_path}"); + return Err("Key already exists.".into()); + } + let key = PKey::generate_ed25519()?; + let mut key_file = File::create(private_key_path)?; + let pem_pkcs8 = key.private_key_to_pem_pkcs8()?; + key_file.write_all(&pem_pkcs8)?; + Ok(String::from_utf8(pem_pkcs8)?) + } + + pub fn get_hratls_pubkey_hex() -> String { + let private_key_pem_str = Self::get_hratls_private_key().0; + let private_key = PKey::private_key_from_pem(private_key_pem_str.as_ref()).unwrap(); + let pubkey = private_key.raw_public_key().unwrap(); + pubkey.iter().fold(String::new(), |acc, x| acc + &format!("{:02X?}", x)) + } + + fn hratls_private_key_path() -> String { + Self::path_dir().unwrap() + ("/hratls_private_key.pem") + } +} + +impl Config { + pub fn get_mr_signer() -> [u8; 32] { + let mut signing_key_mod = Self::get_signing_key().0.n().to_vec(); + signing_key_mod.reverse(); // make it little endian + + let mut hasher = Hasher::new(MessageDigest::sha256()).unwrap(); + hasher.update(&signing_key_mod).unwrap(); + + let mr_signer_raw = hasher.finish().unwrap(); + + let mut mr_signer = [0u8; 32]; + mr_signer.copy_from_slice(&mr_signer_raw[..32]); + + mr_signer + } + + pub fn get_signing_key() -> (Rsa, String) { + let signing_key_pem_str = + std::fs::read_to_string(Self::signing_key_path()).unwrap_or_else(|_| { + Self::create_signing_key().expect("Failed to create enclave signing key") + }); + + (Rsa::private_key_from_pem(signing_key_pem_str.as_ref()).unwrap(), Self::signing_key_path()) + } + + fn create_signing_key() -> Result> { + let signing_key_path = Self::signing_key_path(); + if Path::new(&signing_key_path).exists() { + log::debug!("Found signing_key at {signing_key_path}"); + return Err("Key already exists.".into()); + } + let key = Rsa::generate_with_e(3072, BigNum::from_u32(3)?.as_ref())?; + let mut key_file = File::create(signing_key_path)?; + let pem_pkcs8 = key.private_key_to_pem()?; + key_file.write_all(&pem_pkcs8)?; + Ok(String::from_utf8(pem_pkcs8)?) + } + + fn signing_key_path() -> String { + Self::path_dir().unwrap() + ("/app_signing_key.pem") + } +} + +pub fn get_unified_command_output(output: &std::process::Output) -> String { + format!( + "!!! stdout:\n{}\n!!! stderr:\n{}", + String::from_utf8(output.stdout.clone()) + .unwrap_or("Could not grab stdout from installation script.".to_string()), + String::from_utf8(output.stderr.clone()) + .unwrap_or("Could not grab stderr from installation script.".to_string()) + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_hratls_private_key() { + let hratls_private_key = Config::get_hratls_private_key().0; + println!("hratls_private_key:\n{hratls_private_key}"); + } + + #[test] + fn test_mr_signer() { + let mr_signer = + Config::get_mr_signer().iter().fold(String::new(), |acc, x| acc + &format!("{:X?}", x)); + println!("mr_signer: {mr_signer}",); + } +} diff --git a/src/constants.rs b/src/constants.rs new file mode 100644 index 0000000..e39d4fd --- /dev/null +++ b/src/constants.rs @@ -0,0 +1 @@ +pub const HRATLS_APP_PORT: u32 = 34500; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..6b626db --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,50 @@ +pub mod config; +pub mod constants; +pub mod name_generator; +pub mod operators; +pub mod packagers; +pub mod sgx; +pub mod snp; +pub mod utils; + +use serde::Serialize; + +pub trait HumanOutput { + fn human_cli_print(&self); +} + +#[derive(Serialize)] +pub struct SimpleOutput { + message: String, +} + +impl From<&str> for SimpleOutput { + fn from(message: &str) -> Self { + Self { message: message.to_string() } + } +} + +impl HumanOutput for SimpleOutput { + fn human_cli_print(&self) { + println!("{}", self.message); + } +} + +pub fn cli_print(output: Result>) { + let output = match output { + Ok(output) => output, + Err(e) => { + println!("Error: {e}"); + std::process::exit(1); + } + }; + match std::env::var("FORMAT") { + Ok(format) if format.as_str() == "JSON" => { + println!("{}", serde_json::to_string_pretty(&output).unwrap()) + } + Ok(format) if format.as_str() == "YAML" => { + print!("{}", serde_yaml::to_string(&output).unwrap()) + } + _ => output.human_cli_print(), + }; +} diff --git a/src/name_generator.rs b/src/name_generator.rs new file mode 100644 index 0000000..fdf5fb3 --- /dev/null +++ b/src/name_generator.rs @@ -0,0 +1,202 @@ +#![allow(dead_code)] +use rand::Rng; + +pub fn random_app_name() -> String { + let mut rng = rand::thread_rng(); + let random_index = rng.gen_range(0..77); + let adjective = ADJECTIVES[random_index].to_string(); + let random_index = rng.gen_range(0..97); + let substantive = SUBSTANTIVES[random_index].to_string(); + let app_name = adjective + "-" + &substantive; + app_name +} +pub fn random_vm_name() -> String { + let mut rng = rand::thread_rng(); + let random_index = rng.gen_range(0..77); + let adjective = ADJECTIVES[random_index].to_string(); + let random_index = rng.gen_range(0..97); + let substantive = SUBSTANTIVES[random_index].to_string(); + let vm_name = adjective + "-" + &substantive; + eprintln!("No hostname specified! Using random VM name: {}", vm_name); + vm_name +} + +const ADJECTIVES: [&str; 77] = [ + "ancient", + "arcane", + "astromech", + "brave", + "celestial", + "chaotic", + "charismatic", + "cloaked", + "commanding", + "cosmic", + "cruel", + "cursed", + "malefic", + "defiant", + "devious", + "divine", + "dreadful", + "enchanted", + "energetic", + "eternal", + "ethereal", + "explosive", + "elegant", + "fearsome", + "fierce", + "forbidden", + "fragile", + "frozen", + "futuristic", + "galactic", + "glorious", + "grim", + "haunted", + "heroic", + "hyperspace", + "immortal", + "imperial", + "infernal", + "intergalactic", + "intrepid", + "legendary", + "luminous", + "magical", + "majestic", + "merciless", + "mighty", + "mysterious", + "mystic", + "mystical", + "mythical", + "noble", + "planetary", + "powerful", + "primal", + "radiant", + "rebel", + "relentless", + "resolute", + "ruthless", + "sacred", + "savage", + "sensitive", + "shadowy", + "shimmering", + "starry", + "stealthy", + "strategic", + "swift", + "tactical", + "treacherous", + "undying", + "unyielding", + "vengeful", + "villainous", + "wicked", + "wondrous", + "wretched", +]; + +const SUBSTANTIVES: [&str; 97] = [ + "axe", + "bed", + "bag", + "bit", + "blade", + "hobo", + "blanket", + "blender", + "bolt", + "bowl", + "broom", + "brush", + "bucket", + "cabinet", + "caliper", + "candle", + "chain", + "chair", + "charger", + "chisel", + "clock", + "compass", + "couch", + "foot", + "crowbar", + "curtain", + "door", + "drill", + "faucet", + "file", + "fork", + "frame", + "dog", + "cat", + "fridge", + "grinder", + "hammer", + "handle", + "hanger", + "hinge", + "holster", + "iron", + "kettle", + "knife", + "lamp", + "basket", + "lever", + "mallet", + "mattress", + "microwave", + "mirror", + "mop", + "mug", + "nail", + "pan", + "pickaxe", + "pillow", + "plane", + "plate", + "pot", + "bar", + "punch", + "ratchet", + "river", + "rug", + "bottle", + "jar", + "ruler", + "mailbox", + "sandpaper", + "saw", + "scissors", + "screw", + "screwdriver", + "shears", + "shelf", + "shovel", + "sink", + "soap", + "socket", + "spanner", + "sponge", + "spoon", + "stapler", + "stove", + "table", + "tape", + "thermometer", + "toaster", + "towel", + "trashcan", + "trowel", + "vacuum", + "vase", + "vice", + "whetstone", + "wrench", +]; diff --git a/src/operators.rs b/src/operators.rs new file mode 100644 index 0000000..3231426 --- /dev/null +++ b/src/operators.rs @@ -0,0 +1,92 @@ +use crate::snp::grpc; +use crate::snp::grpc::brain; +use tabled::Tabled; + +#[derive(Tabled)] +struct TabledOperator { + wallet: String, + email: String, + escrow: u64, + app_nodes: u64, + vm_nodes: u64, + reports: u64, +} + +impl From for TabledOperator { + fn from(brain_operator: grpc::brain::ListOperatorsResp) -> Self { + TabledOperator { + wallet: brain_operator.pubkey, + escrow: brain_operator.escrow, + email: brain_operator.email, + app_nodes: brain_operator.app_nodes, + vm_nodes: brain_operator.vm_nodes, + reports: brain_operator.reports, + } + } +} + +pub fn register(escrow: u64, email: String) -> Result { + grpc::block_on(grpc::register_operator(escrow, email))?; + Ok(super::SimpleOutput::from("Successfully registered you as operator.")) +} + +impl super::HumanOutput for brain::InspectOperatorResp { + fn human_cli_print(&self) { + match &self.operator { + Some(op) => { + println!("The operator {} supplies {} nanoLP as escrow,", op.pubkey, op.escrow,); + println!( + "has {} app servers, {} VM servers, and {} total reports for all servers.", + op.app_nodes, op.vm_nodes, op.reports + ); + println!("He can be contacted at {}", op.email); + } + None => (), + } + if self.nodes.len() == 0 { + return; + } + println!("\n-- VM NODES --"); + let mut count = 1; + for node in self.nodes.iter() { + println!("\nNODE #{count}:"); + node.human_cli_print(); + count += 1; + } + } +} + +// TODO: move non-SNP gRPC requrests out of the SNP module (currently in crate::snp::grpc) +// This is an example of why we need refactoring... requesting information about operators is not +// bound in any way to the SNP module, however our gRPC module is stuck in the SNP module. +// We should figure how to architect the CLI so that this is a bit cleaner. +pub fn inspect_operator(wallet: String) -> Result { + grpc::block_on(grpc::inspect_operator(wallet)) +} + +impl super::HumanOutput for Vec { + fn human_cli_print(&self) { + let operators: Vec = self.iter().map(|op| op.clone().into()).collect(); + + let mut table = tabled::Table::new(operators); + table.with(tabled::settings::Style::rounded()); + println!("{table}"); + println!("To join our program a node operator, visit https://detee.ltd"); + } +} + +pub fn print_operators() -> Result, grpc::Error> { + Ok(grpc::block_on(grpc::list_operators())?) +} + +pub fn kick(contract_uuid: String, reason: String) -> Result { + let nano_lp = grpc::block_on(grpc::kick_contract(contract_uuid, reason))?; + Ok(super::SimpleOutput::from( + format!("Successfully terminated contract. Refunded {} nanoLP.", nano_lp).as_str(), + )) +} + +pub fn ban(wallet: String) -> Result { + grpc::block_on(grpc::ban_user(wallet))?; + Ok(super::SimpleOutput::from("Successfully banned user")) +} diff --git a/src/packagers.rs b/src/packagers.rs new file mode 100644 index 0000000..77c964c --- /dev/null +++ b/src/packagers.rs @@ -0,0 +1,50 @@ +use serde::Serialize; +use tabled::Tabled; + +#[derive(Tabled, Serialize)] +pub struct Packager { + username: String, + email: String, + dtrfs: u32, + templates: u32, + app_bundles: u32, +} + +impl crate::HumanOutput for Vec { + fn human_cli_print(&self) { + let mut table = tabled::Table::new(self); + table.with(tabled::settings::Style::rounded()); + println!("{table}"); + println!( + "As you can see, all software available in the network got developed by DeTEE Ltd." + ); + println!("To join our program as software developer, visit https://detee.ltd"); + println!("We are looking forward to your contributions!") + } +} + +pub fn get_packagers() -> Vec { + vec![ + Packager { + username: "gheorghe".to_string(), + email: "gheo@detee.ltd".to_string(), + dtrfs: 3, + templates: 6, + app_bundles: 0, + }, + Packager { + username: "valy".to_string(), + email: "valy@detee.ltd".to_string(), + dtrfs: 0, + templates: 0, + app_bundles: 4, + }, + Packager { + username: "noor".to_string(), + email: "nmohammed@detee.ltd".to_string(), + dtrfs: 0, + templates: 0, + app_bundles: 10, + }, + ] +} diff --git a/src/sgx/cli_handler.rs b/src/sgx/cli_handler.rs new file mode 100644 index 0000000..4d9f650 --- /dev/null +++ b/src/sgx/cli_handler.rs @@ -0,0 +1,189 @@ +use crate::name_generator::random_app_name; +use crate::sgx::config::{validate_yaml, DeteeCliExt}; +use crate::sgx::grpc_brain::{delete_app, new_app}; +use crate::sgx::grpc_dtpm::{attest_and_send_config, get_config_from_enclave}; +use crate::sgx::packaging::package_enclave; +use crate::sgx::AppDeleteResponse; +use crate::utils::block_on; +use crate::{cli_print, SimpleOutput}; +use clap::ArgMatches; +use detee_shared::sgx::types::brain::AppDeployConfig; +use detee_shared::sgx::types::brain::Resource; + +use super::grpc_brain::list_apps; +use super::{get_app_node, AppContract, AppDeployResponse}; + +pub fn handle_app(app_matche: &ArgMatches) { + match app_matche.subcommand() { + Some(("package", subcom_args)) => cli_print(handle_package(subcom_args)), + Some(("deploy", subcom_args)) => cli_print(handle_deploy(subcom_args)), + Some(("delete", subcom_args)) => cli_print(handle_delete(subcom_args)), + Some(("list", subcom_args)) => cli_print(handle_list(subcom_args)), + Some(("config", subcom_args)) => handle_config(subcom_args), + _ => println!("No valid subcommand provided. Use --help for more information."), + } +} + +fn handle_package(package_match: &ArgMatches) -> Result> { + if let Some(file_paths) = package_match.get_many::("files") { + let packaging_items = file_paths.cloned().collect::>(); + let package_type = package_match.get_one::("package-type").unwrap().as_str(); + match package_enclave(packaging_items, package_type) { + Ok(0) => Ok(SimpleOutput::from("Enclave packaged successfully")), + Ok(exit_code) => Err(Box::new(std::io::Error::other(format!( + "Enclave packaging failed with exit code: {exit_code}" + )))), + Err(e) => Err(Box::new(std::io::Error::other(format!( + "Could not package enclave due to error: {e}" + )))), + } + } else { + Err(Box::new(std::io::Error::other("files argument required"))) + } +} + +fn handle_deploy( + deploy_match: &ArgMatches, +) -> Result> { + let mut app_deploy_config = if let Some(file_path) = deploy_match.get_one::("yaml-path") + { + AppDeployConfig::from_path(file_path).unwrap() + } else { + let vcpu = deploy_match.get_one::("vcpus").unwrap().clone(); + let memory_mb = deploy_match.get_one::("memory").unwrap().clone(); + let disk_mb = deploy_match.get_one::("disk").unwrap().clone(); + let port = + deploy_match.get_many::("port").unwrap_or_default().cloned().collect::>(); + let package_url = deploy_match.get_one::("package-url").unwrap().clone(); + let package_type = deploy_match.get_one::("package-type").unwrap().clone(); + let hours = deploy_match.get_one::("hours").unwrap().clone(); + let node_unit_price = deploy_match.get_one::("price").unwrap().clone(); + let location = deploy_match.get_one::("location").unwrap().as_str(); + let app_name = + deploy_match.get_one::("name").cloned().unwrap_or_else(|| random_app_name()); + + let private_package = package_type == "private"; + let resource = Resource { vcpu, memory_mb, disk_mb, port }; + let node_pubkey = match block_on(get_app_node(resource.clone(), location.into())) { + Ok(node) => node.node_pubkey, + Err(e) => { + return Err(Box::new(std::io::Error::other( + format!("Could not get node pubkey due to error: {:?}", e).as_str(), + ))); + } + }; + + AppDeployConfig { + package_url, + resource, + node_unit_price, + hours, + node_pubkey, + private_package, + app_name, + ..Default::default() + } + }; + + if app_deploy_config.app_name == "" { + app_deploy_config.app_name = random_app_name(); + } + + match block_on(new_app(app_deploy_config)) { + Ok(new_app_res) if new_app_res.error == "" => Ok(new_app_res.into()), + Ok(new_app_res) => Err(Box::new(std::io::Error::other(new_app_res.error))), + Err(e) => Err(Box::new(e)), + } +} + +fn handle_delete( + delete_match: &ArgMatches, +) -> Result> { + let app_uuid = delete_match + .get_one::("uuid") + .ok_or_else(|| panic!("uuid argument required")) + .unwrap() + .to_owned(); + + match block_on(delete_app(app_uuid.clone())) { + Ok(_) => Ok(AppDeleteResponse { + uuid: app_uuid, + message: "App deleted successfully".to_string(), + }), + Err(e) => { + Err(Box::new(std::io::Error::other(format!("Could not delete app due to error: {e}")))) + } + } +} + +fn handle_list(_: &ArgMatches) -> Result, Box> { + match block_on(list_apps()) { + Ok(app_contracts) => { + Ok(app_contracts.into_iter().map(AppContract::from).collect::>()) + } + Err(e) => Err(Box::new(e)), + } +} + +fn handle_config(matches: &ArgMatches) { + match matches.subcommand() { + Some(("validate", subcom_args)) => cli_print(handle_config_sub_validate(subcom_args)), + Some(("update", subcom_args)) => cli_print(handle_config_sub_update(subcom_args)), + Some(("get", subcom_args)) => cli_print(handle_config_sub_get(subcom_args)), + _ => { + println!("No valid config subcommand provided."); + } + } +} + +fn handle_config_sub_validate( + check_matche: &ArgMatches, +) -> Result> { + if let Some(file_path) = check_matche.get_one::("config") { + let _config = validate_yaml(&file_path); + Ok(SimpleOutput::from("The YAML file is valid!")) + } else { + Err(Box::new(std::io::Error::other("config file path argument required"))) + } +} + +fn handle_config_sub_update( + update_matche: &ArgMatches, +) -> Result> { + if let (Some(file_path), Some(uuid)) = + (update_matche.get_one::("config"), update_matche.get_one::("uuid")) + { + let loaded_config = validate_yaml(&file_path).unwrap(); + match block_on(attest_and_send_config(loaded_config, uuid)) { + Ok(_) => Ok(SimpleOutput::from("App launch config updated successfully")), + Err(e) => Err(Box::new(std::io::Error::other(format!( + "Could not attest and update app launch config due to error: {e}" + )))), + } + } else { + Err(Box::new(std::io::Error::other("uuid and config arguments required"))) + } +} + +fn handle_config_sub_get( + get_matche: &ArgMatches, +) -> Result> { + if let (Some(file_path_to_save), Some(uuid)) = + (get_matche.get_one::("path"), get_matche.get_one::("uuid")) + { + match block_on(get_config_from_enclave(uuid)) { + Ok(config) => { + let config_yaml = serde_yaml::to_string(&config).unwrap(); + std::fs::write(file_path_to_save, config_yaml).unwrap(); + Ok(SimpleOutput::from( + format!("enclave config saved to: {file_path_to_save}").as_str(), + )) + } + Err(e) => Err(Box::new(std::io::Error::other(format!( + "Could not get enclave config due to error: {e}" + )))), + } + } else { + Err(Box::new(std::io::Error::other("path and uuid arguments required"))) + } +} diff --git a/src/sgx/config.rs b/src/sgx/config.rs new file mode 100644 index 0000000..9633a36 --- /dev/null +++ b/src/sgx/config.rs @@ -0,0 +1,34 @@ +use detee_shared::sgx::types::{brain::AppDeployConfig, dtpm::DtpmConfig}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Disk Access Error: {0}")] + DiskAccess(#[from] std::io::Error), + #[error("Yaml Error: {0}")] + SerdeYaml(#[from] serde_yaml::Error), + #[error("App launch config Error: {0}")] + DtpmConfig(#[from] detee_shared::sgx::types::dtpm::Error), +} + +type Result = std::result::Result; + +#[allow(dead_code)] +pub trait DeteeCliExt { + fn from_path(path: &String) -> Result + where + Self: std::marker::Sized; +} + +impl DeteeCliExt for AppDeployConfig { + fn from_path(path: &String) -> Result { + let config_str = std::fs::read_to_string(path)?; + Ok(serde_yaml::from_str(&config_str)?) + } +} + +pub fn validate_yaml(file_path: &str) -> Result { + let parsed_config = DtpmConfig::from_path(file_path)?; + let loaded_config = parsed_config.clone().load_data()?; + + Ok(loaded_config) +} diff --git a/src/sgx/grpc_brain.rs b/src/sgx/grpc_brain.rs new file mode 100644 index 0000000..e56ed31 --- /dev/null +++ b/src/sgx/grpc_brain.rs @@ -0,0 +1,89 @@ +use detee_shared::sgx::pb::brain::brain_app_cli_client::BrainAppCliClient; +use detee_shared::sgx::pb::brain::{ + AppContract, AppNodeFilters, AppNodeListResp, DelAppReq, ListAppContractsReq, NewAppReq, + NewAppRes, +}; +use detee_shared::sgx::types::brain::AppDeployConfig; +use tokio_stream::StreamExt; + +use crate::config::Config; +use crate::utils::{self, calculate_nanolp_for_app, mr_enclave_from_public_registry, sign_request}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Failed to connect to the brain: {0}")] + BrainConnection(#[from] tonic::transport::Error), + #[error("Received error from brain: {}", _0.message())] + ResponseStatus(#[from] tonic::Status), + #[error(transparent)] + InternalError(#[from] utils::Error), + #[error(transparent)] + ConfigError(#[from] crate::config::Error), +} + +type Result = std::result::Result; + +pub async fn new_app(app_deploy_config: AppDeployConfig) -> Result { + let resource = app_deploy_config.clone().resource; + let mut req: NewAppReq = app_deploy_config.clone().into(); + + let locked_nano = calculate_nanolp_for_app( + resource.vcpu, + resource.memory_mb, + resource.disk_mb, + app_deploy_config.hours, + req.price_per_unit, + ); + + req.uuid = "".to_string(); + req.locked_nano = locked_nano; + req.admin_pubkey = Config::get_detee_wallet().expect("No wallet found"); + req.hratls_pubkey = Config::get_hratls_pubkey_hex(); + + if !app_deploy_config.private_package && app_deploy_config.public_package_mr_enclave.is_none() { + let mr_enclave: [u8; 32] = mr_enclave_from_public_registry(&req.package_url).await?; + req.public_package_mr_enclave = Some(mr_enclave.to_vec()); + } + + let mut daemon_serivce = BrainAppCliClient::connect(Config::get_brain_url()).await?; + let res = daemon_serivce.deploy_app(sign_request(req)?).await?; + Ok(res.into_inner()) +} + +pub async fn delete_app(app_uuid: String) -> Result<()> { + let admin_pubkey = Config::get_detee_wallet()?; + let delete_req = DelAppReq { uuid: app_uuid, admin_pubkey }; + let mut daemon_serivce = BrainAppCliClient::connect(Config::get_brain_url()).await?; + let _ = daemon_serivce.delete_app(sign_request(delete_req)?).await?; + Ok(()) +} + +pub async fn list_apps() -> Result> { + let admin_pubkey = Config::get_detee_wallet().unwrap(); + let list_req = ListAppContractsReq { admin_pubkey }; + let mut daemon_serivce = BrainAppCliClient::connect(Config::get_brain_url()).await?; + let mut res_stream = + daemon_serivce.list_app_contracts(sign_request(list_req)?).await?.into_inner(); + + let mut app_contracts = vec![]; + + while let Some(stream_update) = res_stream.next().await { + match stream_update { + Ok(contract) => { + app_contracts.push(contract); + } + Err(e) => { + println!("Brain disconnected from register_node: {e}"); + } + } + } + + Ok(app_contracts) +} + +pub async fn get_one_app_node(req: AppNodeFilters) -> Result { + let mut daemon_serivce = BrainAppCliClient::connect(Config::get_brain_url()).await?; + let res = daemon_serivce.get_one_app_node(sign_request(req)?).await?; + + Ok(res.into_inner()) +} diff --git a/src/sgx/grpc_dtpm.rs b/src/sgx/grpc_dtpm.rs new file mode 100644 index 0000000..83daefe --- /dev/null +++ b/src/sgx/grpc_dtpm.rs @@ -0,0 +1,106 @@ +use detee_sgx::{prelude::*, HRaTlsConfigBuilder}; +use detee_shared::sgx::pb::dtpm::DtpmGetConfigReq; +use hyper_rustls::HttpsConnectorBuilder; +use rustls::ClientConfig; +use std::sync::{Arc, RwLock}; +use tonic::transport::{Channel, Endpoint}; + +use detee_shared::sgx::{ + pb::dtpm::{ + dtpm_config_manager_client::DtpmConfigManagerClient, DtpmConfigData, DtpmSetConfigReq, + }, + types::dtpm::DtpmConfig, +}; + +use crate::{config::Config, utils::hratls_url_and_mr_enclave_from_app_id}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Failed to connect to the brain: {0}")] + BrainConnection(#[from] tonic::transport::Error), + #[error("Received error from brain: {}", _0.message())] + ResponseStatus(#[from] tonic::Status), + #[error("Hex: {0}")] + HexDecode(#[from] hex::FromHexError), + #[error("Disk access error: {0}")] + DiskAccess(#[from] std::io::Error), + #[error("HRatls: {0}")] + SgxHRatls(#[from] detee_sgx::error::SgxError), + #[error("DtpmConfig: {0}")] + DtpmConfig(String), +} + +type Result = std::result::Result; + +pub async fn connect_dtpm_grpc_client( + hratls_uri: String, + package_mr_enclave: Option<[u8; 32]>, +) -> Result> { + let private_key_pem = Config::get_hratls_private_key().0; + let mr_signer = vec![Config::get_mr_signer()]; + + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); + + let hratls_config = Arc::new(RwLock::new( + HRaTlsConfig::new() + .allow_instance_measurement(InstanceMeasurement::new().with_mrsigners(mr_signer)) + .with_hratls_private_key_pem(private_key_pem), + )); + + if let Some(mr_enclave) = package_mr_enclave { + hratls_config.write().unwrap().allow_more_instance_measurement( + InstanceMeasurement::new().with_mrenclaves(vec![mr_enclave]), + ); + } + + let client_tls_config = ClientConfig::from_hratls_config(hratls_config.clone())?; + let connector = HttpsConnectorBuilder::new() + .with_tls_config(client_tls_config) + .https_only() + .enable_http2() + .build(); + + let channel = Endpoint::from_shared(hratls_uri)?.connect_with_connector(connector).await?; + + Ok(DtpmConfigManagerClient::new(channel)) +} + +pub async fn attest_and_send_config(loaded_config: DtpmConfig, uuid: &str) -> Result<()> { + let config_data = Some(DtpmConfigData::from(loaded_config)); + let req_data = DtpmSetConfigReq { config_data, ..Default::default() }; + + log::trace!("Decoded the configuration... {:?}", req_data); + + let (hratls_uri, mr_enclave) = hratls_url_and_mr_enclave_from_app_id(uuid).await; + log::info!("hratls uri: {}\nmr_enclave: {:?}", &hratls_uri, &mr_enclave); + + let client = connect_dtpm_grpc_client(hratls_uri, mr_enclave).await?; + + let response = client + .max_decoding_message_size(10240000) + .set_config(tonic::Request::new(req_data)) + .await?; + + log::trace!("Received respose from the server...{:?}", response.into_inner()); + + Ok(()) +} + +pub async fn get_config_from_enclave(uuid: &str) -> Result { + let (hratls_uri, mr_enclave) = hratls_url_and_mr_enclave_from_app_id(uuid).await; + log::info!("hratls uri: {}\nmr_enclave: {:?}", &hratls_uri, &mr_enclave); + + let client = connect_dtpm_grpc_client(hratls_uri, None).await?; + + let mgr_config_pb = client + .max_decoding_message_size(10240000) + .get_config(tonic::Request::new(DtpmGetConfigReq { empty: None })) + .await? + .into_inner(); + + let config: DtpmConfig = mgr_config_pb + .config_data + .ok_or(Error::DtpmConfig("config data not found".to_string()))? + .into(); + Ok(config) +} diff --git a/src/sgx/mod.rs b/src/sgx/mod.rs new file mode 100644 index 0000000..039bc77 --- /dev/null +++ b/src/sgx/mod.rs @@ -0,0 +1,165 @@ +use detee_shared::sgx::{ + pb::brain::{ + AppContract as AppContractPB, AppNodeFilters, AppNodeListResp, AppResource, NewAppRes, + }, + types::brain::Resource, +}; +use grpc_brain::get_one_app_node; + +use serde::{Deserialize, Serialize}; +use tabled::Tabled; + +use crate::{constants::HRATLS_APP_PORT, utils::block_on}; + +pub mod cli_handler; +pub mod config; +pub mod grpc_brain; +pub mod grpc_dtpm; +pub mod packaging; + +#[derive(Tabled, Debug, Serialize, Deserialize)] +pub struct AppContract { + #[tabled(rename = "Location")] + pub location: String, + #[tabled(rename = "UUID")] + pub uuid: String, + pub name: String, + #[tabled(rename = "Cores")] + pub vcpu: u32, + #[tabled(rename = "Mem (MB)")] + pub memory_mb: u32, + #[tabled(rename = "Disk (MB)")] + pub disk_mb: u32, + #[tabled(rename = "LP/h")] + pub cost_h: String, + #[tabled(rename = "time left", display_with = "display_mins")] + pub time_left: u64, + #[tabled(rename = "Node IP")] + pub node_ip: String, + #[tabled(rename = "Exposed ports", display_with = "display_ports")] + pub exposed_host_ports: Vec<(u32, u32)>, +} + +fn display_mins(minutes: &u64) -> String { + let mins = minutes % 60; + let hours = minutes / 60; + + format!("{hours}h {mins}m") +} + +fn display_ports(ports: &Vec<(u32, u32)>) -> String { + ports.iter().map(|port| format!("({}:{})", port.0, port.1,)).collect::>().join(", ") +} + +impl crate::HumanOutput for Vec { + fn human_cli_print(&self) { + let style = tabled::settings::Style::rounded(); + let mut table = tabled::Table::new(self); + table.with(style); + println!("{table}"); + } +} + +impl From for AppContract { + fn from(brain_app_contract: AppContractPB) -> Self { + let node_pubkey = brain_app_contract.node_pubkey.clone(); + let location = match block_on(get_one_app_node(AppNodeFilters { + node_pubkey: node_pubkey.clone(), + ..Default::default() + })) { + Ok(node) => format!("{}, {} ({})", node.city, node.region, node.country), + Err(e) => { + log::warn!("Could not get information about node {node_pubkey} fram brain: {e:?}"); + String::new() + } + }; + + let AppResource { vcpu, memory_mb, disk_mb, .. } = + brain_app_contract.resource.unwrap_or_default(); + + let exposed_host_ports = brain_app_contract + .mapped_ports + .iter() + .map(|port| (port.host_port, port.app_port)) + .collect::>(); + + Self { + location, + uuid: brain_app_contract.uuid, + name: brain_app_contract.app_name, + vcpu, + memory_mb, + disk_mb, + cost_h: format!( + "{:.4}", + (brain_app_contract.nano_per_minute * 60) as f64 / 1_000_000_000.0 + ), + time_left: brain_app_contract.locked_nano / brain_app_contract.nano_per_minute, + node_ip: brain_app_contract.public_ipv4, + exposed_host_ports, + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct AppDeployResponse { + pub status: String, + pub uuid: String, + pub name: String, + pub node_ip: String, + pub hratls_port: u32, + pub error: String, +} + +impl crate::HumanOutput for AppDeployResponse { + fn human_cli_print(&self) { + println!("App deployd with UUID: {}", self.uuid); + } +} + +impl From for AppDeployResponse { + fn from(value: NewAppRes) -> Self { + Self { + status: value.status, + uuid: value.uuid, + name: "".to_string(), + node_ip: value.ip_address, + hratls_port: value + .mapped_ports + .iter() + .find(|port| port.app_port == HRATLS_APP_PORT) + .map(|port| port.host_port) + .unwrap_or(HRATLS_APP_PORT), + error: value.error, + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct AppDeleteResponse { + pub uuid: String, + pub message: String, +} + +impl crate::HumanOutput for AppDeleteResponse { + fn human_cli_print(&self) { + println!("App deleted successfully: UUID: {}", self.uuid); + } +} + +pub async fn get_app_node( + resource: Resource, + location: crate::snp::deploy::Location, +) -> Result { + let app_node_filter = AppNodeFilters { + vcpus: resource.vcpu, + memory_mb: resource.memory_mb, + storage_mb: resource.disk_mb, + country: location.country.clone().unwrap_or_default(), + region: location.region.clone().unwrap_or_default(), + city: location.city.clone().unwrap_or_default(), + ip: location.node_ip.clone().unwrap_or_default(), + node_pubkey: String::new(), + }; + get_one_app_node(app_node_filter).await +} diff --git a/src/sgx/packaging.rs b/src/sgx/packaging.rs new file mode 100644 index 0000000..be5d3f8 --- /dev/null +++ b/src/sgx/packaging.rs @@ -0,0 +1,36 @@ +use crate::config::Config; +use std::process::Command; + +pub fn package_enclave( + items: Vec, + package_type: &str, +) -> Result> { + let package_items = items + .into_iter() + .map(|item| item.strip_prefix("./").unwrap_or(&item).to_owned()) + .collect::>() + .join(" "); + + let signing_key_path = Config::get_signing_key().1; + let hratls_key_path = Config::get_hratls_private_key().1; + + let docker_package_str = if package_type == "public" { + format!( + r#"docker run --rm -it -v ./:/app/ \ + -v {signing_key_path}:/keys/app_signing_key.pem:ro \ + noormohammedb/enclave_packager_01:pub_v1 {package_items}"# + ) + } else { + format!( + r#"docker run --rm -it -v ./:/app/ \ + -v {signing_key_path}:/keys/app_signing_key.pem:ro \ + -v {hratls_key_path}:/keys/hratls_private_key.pem:ro \ + noormohammedb/enclave_packager_01:v1 {package_items}"# + ) + }; + + let mut child = Command::new("sh").arg("-c").arg(docker_package_str).spawn()?; + + let exit = child.wait()?; + Ok(exit.code().ok_or("Could not get exit code")?) +} diff --git a/src/snp/deploy.rs b/src/snp/deploy.rs new file mode 100644 index 0000000..1d33caf --- /dev/null +++ b/src/snp/deploy.rs @@ -0,0 +1,183 @@ +use super::{ + grpc::{self, block_on, brain}, + injector, Distro, Dtrfs, Error, VmSshArgs, DEFAULT_ARCHLINUX, DEFAULT_DTRFS, +}; +use crate::config::Config; +use log::{debug, info}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +pub enum IPv4Config { + PublishPorts(Vec), + PublicIPv4, +} + +#[derive(Serialize, Deserialize, Default)] +pub struct Location { + pub node_ip: Option, + pub country: Option, + pub region: Option, + pub city: Option, +} + +impl From<&str> for Location { + fn from(s: &str) -> Self { + match s { + "Canada" => Self { country: Some("CA".to_string()), ..Default::default() }, + "Montreal" => Self { city: Some("Montréal".to_string()), ..Default::default() }, + "Vancouver" => Self { city: Some("Vancouver".to_string()), ..Default::default() }, + "US" => Self { country: Some("US".to_string()), ..Default::default() }, + "California" => Self { country: Some("US".to_string()), ..Default::default() }, + "France" => Self { country: Some("FR".to_string()), ..Default::default() }, + "GB" => Self { country: Some("GB".to_string()), ..Default::default() }, + "Random" => Self { ..Default::default() }, + "DE" => Self { country: Some("DE".to_string()), ..Default::default() }, + _ => Self { city: Some("Vancouver".to_string()), ..Default::default() }, + } + } +} + +#[derive(Serialize, Deserialize)] +pub struct Request { + pub hostname: String, + pub hours: u32, + // price per unit per minute + pub price: u64, + pub location: Location, + pub ipv4: IPv4Config, + pub public_ipv6: bool, + pub vcpus: u32, + pub memory_mb: u32, + pub disk_size_gb: u32, + pub dtrfs: Option, + pub distro: Option, +} + +impl Request { + pub fn load_from_yaml(config_path: &str) -> Result { + let new_vm_config = Self::load_from_file(config_path)?; + new_vm_config.deploy() + } + + fn load_from_file(config_path: &str) -> Result { + let content = std::fs::read_to_string(config_path)?; + let new_vm_config: Self = serde_yaml::from_str(&content)?; + Ok(new_vm_config) + } + + pub fn deploy(&self) -> Result { + let (node_ip, new_vm_resp) = self.send_vm_request()?; + info!("Got confirmation from the node {node_ip} that VM started."); + debug!("IPs and ports assigned by node are: {new_vm_resp:#?}"); + if !new_vm_resp.error.is_empty() { + return Err(Error::Node(new_vm_resp.error)); + } + let (kernel_sha, dtrfs_sha) = match self.dtrfs.clone() { + Some(dtrfs) => (dtrfs.kernel_sha, dtrfs.dtrfs_sha), + None => (DEFAULT_DTRFS.kernel_sha.clone(), DEFAULT_DTRFS.dtrfs_sha.clone()), + }; + let args = new_vm_resp.args.ok_or(Error::NoMeasurement)?; + let measurement_args = injector::Args { + uuid: new_vm_resp.uuid.clone(), + hostname: self.hostname.clone(), + vcpus: self.vcpus, + kernel: kernel_sha, + initrd: dtrfs_sha, + args: args.clone(), + }; + let measurement = measurement_args.get_measurement()?; + let (template_url, template_sha) = match &self.distro { + Some(distro) => (distro.template_url.clone(), distro.template_sha.clone()), + None => { + (DEFAULT_ARCHLINUX.template_url.clone(), DEFAULT_ARCHLINUX.template_sha.clone()) + } + }; + let mut ssh_args = injector::execute( + measurement, + args.dtrfs_api_endpoint.clone(), + Some((&template_url, &template_sha)), + &self.hostname, + )?; + ssh_args.uuid = new_vm_resp.uuid; + ssh_args.hostname = self.hostname.clone(); + let _ = super::append_uuid_list(&ssh_args.uuid, &ssh_args.hostname); + Ok(ssh_args) + } + + // returns node IP and data regarding the new VM + fn send_vm_request(&self) -> Result<(String, brain::NewVmResp), Error> { + let admin_pubkey = Config::get_detee_wallet()?; + let node = self.get_node()?; + let (extra_ports, public_ipv4): (Vec, bool) = match &self.ipv4 { + IPv4Config::PublishPorts(vec) => (vec.to_vec(), false), + IPv4Config::PublicIPv4 => (Vec::new(), true), + }; + let (kernel_url, kernel_sha, dtrfs_url, dtrfs_sha) = match self.dtrfs.clone() { + Some(dtrfs) => (dtrfs.kernel_url, dtrfs.kernel_sha, dtrfs.dtrfs_url, dtrfs.dtrfs_sha), + None => ( + DEFAULT_DTRFS.kernel_url.clone(), + DEFAULT_DTRFS.kernel_sha.clone(), + DEFAULT_DTRFS.dtrfs_url.clone(), + DEFAULT_DTRFS.dtrfs_sha.clone(), + ), + }; + let locked_nano = super::calculate_nanolp( + self.vcpus, + self.memory_mb, + self.disk_size_gb, + public_ipv4, + self.hours, + self.price, + ); + let brain_req = brain::NewVmReq { + uuid: String::new(), + hostname: self.hostname.clone(), + admin_pubkey, + node_pubkey: node.node_pubkey, + extra_ports, + public_ipv4, + public_ipv6: self.public_ipv6, + disk_size_gb: self.disk_size_gb, + vcpus: self.vcpus, + memory_mb: self.memory_mb, + kernel_url, + kernel_sha, + dtrfs_url, + dtrfs_sha, + price_per_unit: self.price, + locked_nano, + }; + let new_vm_resp = block_on(grpc::create_vm(brain_req))?; + if !new_vm_resp.error.is_empty() { + return Err(Error::Node(new_vm_resp.error)); + } + Ok((node.ip, new_vm_resp)) + } + + pub fn get_node(&self) -> Result { + let (free_ports, offers_ipv4) = match &self.ipv4 { + IPv4Config::PublishPorts(vec) => (vec.len() as u32, false), + IPv4Config::PublicIPv4 => (0, true), + }; + let filters = brain::VmNodeFilters { + free_ports, + offers_ipv4, + offers_ipv6: self.public_ipv6, + vcpus: self.vcpus, + memory_mb: self.memory_mb, + storage_gb: self.disk_size_gb, + country: self.location.country.clone().unwrap_or_default(), + region: self.location.region.clone().unwrap_or_default(), + city: self.location.city.clone().unwrap_or_default(), + ip: self.location.node_ip.clone().unwrap_or_default(), + node_pubkey: String::new(), + }; + match block_on(grpc::get_one_node(filters)) { + Ok(node) => Ok(node), + Err(e) => { + log::error!("Coult not get node from brain: {e:?}"); + Err(Error::NoValidNodeFound) + } + } + } +} diff --git a/src/snp/grpc.rs b/src/snp/grpc.rs new file mode 100644 index 0000000..7da29f2 --- /dev/null +++ b/src/snp/grpc.rs @@ -0,0 +1,375 @@ +pub mod brain { + tonic::include_proto!("vm_proto"); +} + +use crate::config::Config; +use crate::snp::brain::Account; +use crate::snp::brain::AccountBalance; +use crate::snp::brain::InspectOperatorResp; +use crate::snp::brain::Pubkey; +use brain::{ + brain_cli_client::BrainCliClient, BanUserReq, DeleteVmReq, Empty, ExtendVmReq, KickReq, + ListOperatorsResp, ListVmContractsReq, NewVmReq, NewVmResp, RegOperatorReq, ReportNodeReq, + UpdateVmReq, UpdateVmResp, VmContract, VmNodeFilters, VmNodeListResp, +}; +use lazy_static::lazy_static; +use log::{debug, info, warn}; +use tokio_stream::StreamExt; +use tonic::metadata::errors::InvalidMetadataValue; +use tonic::metadata::AsciiMetadataValue; +use tonic::Request; + +lazy_static! { + static ref SECURE_PUBLIC_KEY: String = use_default_string(); +} + +fn use_default_string() -> String { + "ThisIsMyEternalClient".to_string() +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Failed to connect to the brain: {0}")] + BrainConnection(#[from] tonic::transport::Error), + #[error("Received error from brain: status: {}, message: {}", + _0.code().to_string(), _0.message())] + ResponseStatus(#[from] tonic::Status), + #[error(transparent)] + ConfigError(#[from] crate::config::Error), + #[error("Could not find contract {0}")] + VmContractNotFound(String), + #[error(transparent)] + InternalError(#[from] InvalidMetadataValue), +} + +impl crate::HumanOutput for VmContract { + fn human_cli_print(&self) { + println!( + "The VM {} has the UUID {}, and it runs on the node {}", + self.hostname, self.uuid, self.node_pubkey + ); + if self.public_ipv4.is_empty() { + println!( + "The VM has no public IPv4. The ports published by the VM are: {:?}", + self.exposed_ports + ); + } else { + println!("The Public IPv4 address of the VM is: {}", self.public_ipv4); + } + if self.public_ipv6.is_empty() { + println!("The VM does not have a public IPv6 address."); + } else { + println!("The Public IPv6 address of the VM is: {}", self.public_ipv6); + } + println!( + "The VM has {} vCPUS, {}MB of memory and a disk of {} GB.", + self.vcpus, self.memory_mb, self.disk_size_gb + ); + println!("You have locked {} nanoLP in the contract, that get collected at a rate of {} nanoLP per minute.", + self.locked_nano, self.nano_per_minute); + } +} + +impl crate::HumanOutput for VmNodeListResp { + fn human_cli_print(&self) { + println!("The pubkey of this node is {} and the IP is {}", self.node_pubkey, self.ip); + println!("It belongs to the operator {}", self.operator); + println!( + "This node is located in the city {}, within the region of {}, in {}", + self.city, self.region, self.country + ); + println!("The price multiplier for the node is {}.", self.price); + } +} + +fn sign_request(req: T) -> Result, Error> { + let pubkey = Config::get_detee_wallet()?; + let timestamp = chrono::Utc::now().to_rfc3339(); + let signature = Config::try_sign_message(&format!("{timestamp}{req:?}"))?; + let timestamp: AsciiMetadataValue = timestamp.parse()?; + let pubkey: AsciiMetadataValue = pubkey.parse()?; + let signature: AsciiMetadataValue = signature.parse()?; + let mut req = Request::new(req); + req.metadata_mut().insert("timestamp", timestamp); + req.metadata_mut().insert("pubkey", pubkey); + req.metadata_mut().insert("request-signature", signature); + Ok(req) +} + +pub async fn get_node_list(req: VmNodeFilters) -> Result, Error> { + debug!("Getting nodes from brain..."); + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + let mut nodes = Vec::new(); + let mut grpc_stream = client.list_vm_nodes(sign_request(req)?).await?.into_inner(); + while let Some(stream_update) = grpc_stream.next().await { + match stream_update { + Ok(node) => { + debug!("Received node from brain: {node:?}"); + nodes.push(node); + } + Err(e) => { + warn!("Received error instead of node list: {e:?}"); + } + } + } + debug!("Brain terminated list_nodes stream."); + Ok(nodes) +} + +pub async fn get_balance(account: &str) -> Result { + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + let response = + client.get_balance(sign_request(Pubkey { pubkey: account.to_string() })?).await?; + log::info!("Received account from brain: {response:?}"); + Ok(response.into_inner()) +} + +pub async fn admin_airdrop(pubkey: String, tokens: u64) -> Result<(), Error> { + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + let req = sign_request(brain::AirdropReq { pubkey, tokens })?; + let _ = client.airdrop(req).await?; + Ok(()) +} + +pub async fn admin_slash(pubkey: String, tokens: u64) -> Result<(), Error> { + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + let req = sign_request(brain::SlashReq { pubkey, tokens })?; + let _ = client.slash(req).await?; + Ok(()) +} + +pub async fn get_one_node(req: VmNodeFilters) -> Result { + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + let response = client.get_one_vm_node(sign_request(req)?).await?; + Ok(response.into_inner()) +} + +pub async fn create_vm(req: NewVmReq) -> Result { + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + debug!("Sending NewVmReq to brain: {req:?}"); + match client.new_vm(sign_request(req)?).await { + Ok(resp) => Ok(resp.into_inner()), + Err(e) => Err(e.into()), + } +} + +pub async fn report_node( + node_pubkey: String, + contract: String, + reason: String, +) -> Result<(), Error> { + debug!("Getting contracts from brain..."); + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + client + .report_node(sign_request(ReportNodeReq { + admin_pubkey: Config::get_detee_wallet()?, + node_pubkey, + contract, + reason, + })?) + .await?; + Ok(()) +} + +pub async fn inspect_operator(wallet: String) -> Result { + debug!("Getting information about operator {wallet} from brain."); + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + Ok(client.inspect_operator(Pubkey { pubkey: wallet }).await?.into_inner()) +} + +pub async fn list_operators() -> Result, Error> { + debug!("Getting contracts from brain..."); + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + let mut operators = Vec::new(); + let mut grpc_stream = client.list_operators(sign_request(Empty {})?).await?.into_inner(); + while let Some(stream_update) = grpc_stream.next().await { + match stream_update { + Ok(op) => { + operators.push(op); + } + Err(e) => { + warn!("Received error instead of operators: {e:?}"); + } + } + } + debug!("Brain terminated list_operators stream."); + Ok(operators) +} + +pub async fn register_operator(escrow: u64, email: String) -> Result<(), Error> { + debug!("Connecting to brain to register operator..."); + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + client + .register_operator(sign_request(RegOperatorReq { + pubkey: Config::get_detee_wallet()?, + escrow, + email, + })?) + .await?; + Ok(()) +} + +pub async fn kick_contract(contract_uuid: String, reason: String) -> Result { + debug!("gRPC module: connecting to brain and kicking contract {contract_uuid} for reason: {reason}"); + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + Ok(client + .kick_contract(sign_request(KickReq { + operator_wallet: Config::get_detee_wallet()?, + contract_uuid, + reason, + })?) + .await? + .into_inner() + .nano_lp) +} + +pub async fn ban_user(user_wallet: String) -> Result<(), Error> { + debug!("Connecting to brain to ban user..."); + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + client + .ban_user(sign_request(BanUserReq { + operator_wallet: Config::get_detee_wallet()?, + user_wallet, + })?) + .await?; + Ok(()) +} + +pub async fn list_contracts(req: ListVmContractsReq) -> Result, Error> { + debug!("Getting contracts from brain..."); + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + let mut contracts = Vec::new(); + let mut grpc_stream = client.list_vm_contracts(sign_request(req)?).await?.into_inner(); + while let Some(stream_update) = grpc_stream.next().await { + match stream_update { + Ok(c) => { + info!("Received contract from brain: {c:?}"); + contracts.push(c); + } + Err(e) => { + warn!("Received error instead of contracts: {e:?}"); + } + } + } + debug!("Brain terminated list_contracts stream."); + Ok(contracts) +} + +pub async fn admin_list_contracts() -> Result, Error> { + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + let mut contracts = Vec::new(); + let mut grpc_stream = + client.list_all_vm_contracts(sign_request(brain::Empty {})?).await?.into_inner(); + while let Some(stream_update) = grpc_stream.next().await { + match stream_update { + Ok(contract) => { + info!("Received contract from brain: {contract:?}"); + contracts.push(contract); + } + Err(e) => { + warn!("Received error instead of contracts: {e:?}"); + } + } + } + debug!("Brain terminated list_contracts stream."); + Ok(contracts) +} + +pub async fn admin_list_accounts() -> Result, Error> { + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + let mut accounts = Vec::new(); + let mut grpc_stream = client.list_accounts(sign_request(brain::Empty {})?).await?.into_inner(); + while let Some(stream_update) = grpc_stream.next().await { + match stream_update { + Ok(account) => { + info!("Received account from brain: {account:?}"); + accounts.push(account); + } + Err(e) => { + warn!("Received error instead of contracts: {e:?}"); + } + } + } + debug!("Brain terminated list_contracts stream."); + Ok(accounts) +} + +pub async fn delete_vm(uuid: &str) -> Result<(), Error> { + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + let req = DeleteVmReq { uuid: uuid.to_string(), admin_pubkey: Config::get_detee_wallet()? }; + let result = client.delete_vm(sign_request(req)?).await; + match result { + Ok(confirmation) => { + log::debug!("VM deletion confirmation: {confirmation:?}"); + } + Err(e) => { + log::error!("Could not delete vm: {e:?}"); + return Err(e.into()); + } + }; + Ok(()) +} + +pub async fn extend_vm(uuid: String, admin_pubkey: String, locked_nano: u64) -> Result<(), Error> { + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + let req = ExtendVmReq { admin_pubkey, uuid, locked_nano }; + let result = client.extend_vm(sign_request(req)?).await; + match result { + Ok(confirmation) => { + log::debug!("VM contract extension confirmation: {confirmation:?}"); + log::info!( + "VM contract got updated. It now has {} LP locked for the VM.", + locked_nano as f64 / 1_000_000_000.0 + ); + } + Err(e) => { + log::debug!("Got error from brain: {:?}", e); + log::error!("Could not extend VM contract: {}", e.message()); + return Err(e.into()); + } + }; + Ok(()) +} + +pub async fn update_vm(req: UpdateVmReq) -> Result { + info!("Updating VM {req:?}"); + let mut client = BrainCliClient::connect(Config::get_brain_url()).await?; + let result = client.update_vm(sign_request(req)?).await; + match result { + Ok(resp) => { + let resp = resp.into_inner(); + if resp.error.is_empty() { + info!("Got VM update response: {resp:?}"); + Ok(resp) + } else { + debug!("Got VM update error: {:?}", resp); + Ok(resp) + } + } + Err(e) => { + log::error!("Could not update vm: {e:?}"); + Err(e.into()) + } + } +} + +pub async fn get_contract_by_uuid(uuid: &str) -> Result { + let req = brain::ListVmContractsReq { + wallet: Config::get_detee_wallet()?, + uuid: uuid.to_string(), + ..Default::default() + }; + let contracts = list_contracts(req).await?; + if contracts.is_empty() { + log::error!("Could not find any contract by ID {uuid}"); + return Err(Error::VmContractNotFound(uuid.to_string())); + } + Ok(contracts[0].clone()) +} + +pub fn block_on(future: F) -> F::Output +where + F: std::future::Future, +{ + tokio::runtime::Runtime::new().unwrap().block_on(future) +} diff --git a/src/snp/injector.rs b/src/snp/injector.rs new file mode 100644 index 0000000..6f896db --- /dev/null +++ b/src/snp/injector.rs @@ -0,0 +1,147 @@ +use crate::{config::Config, snp::grpc::brain}; +use log::debug; +use std::net::IpAddr; + +#[derive(Debug)] +pub struct Args { + pub uuid: String, + pub hostname: String, + pub vcpus: u32, + pub kernel: String, + pub initrd: String, + pub args: brain::MeasurementArgs, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Failed to execute: {0}")] + FailedExecution(String), + #[error("Disk access error: {0}")] + DiskAccess(#[from] std::io::Error), + #[error("Could not parse kernel parameters sent by deamon")] + AlteredKernelParams, + #[error("Could not inject secrets to VM: {0}")] + InjectorError(String), + #[error(transparent)] + ConfigError(#[from] crate::config::Error), + #[error("The endpoint returned by the daemon is not valid: {0}")] + InvalidVmAddress(String), +} + +impl From for Error { + fn from(_: std::net::AddrParseError) -> Self { + Self::AlteredKernelParams + } +} + +impl From for Error { + fn from(_: std::num::ParseIntError) -> Self { + Self::AlteredKernelParams + } +} + +impl Args { + pub fn get_measurement(&self) -> Result { + log::debug!("Measurement args: {self:#?}"); + let params = self.kernel_params()?; + Config::verify_and_install_artefacts(&self.args.ovmf_hash)?; + Config::verify_and_install_artefacts(&self.initrd)?; + Config::verify_and_install_artefacts(&self.kernel)?; + let artefacts_dir = Config::artefacts_dir()? + "/"; + let script_result = std::process::Command::new("sev-snp-measure.py") + .arg("--mode=snp") + .arg(format!("--vcpus={}", self.vcpus)) + .arg("--vcpu-type=EPYC-v4") + .arg("--output-format=hex") + .arg(format!("--ovmf={}", artefacts_dir.clone() + &self.args.ovmf_hash)) + .arg(format!("--kernel={}", artefacts_dir.clone() + &self.kernel)) + .arg(format!("--initrd={}", artefacts_dir + &self.initrd)) + .arg(format!("--append={}", params)) + .output() + .map_err(|e| { + Error::FailedExecution(format!("Failed to execute sev-snp-measure.py: {e:?}")) + })?; + if !script_result.status.success() { + return Err(Error::FailedExecution(format!( + "sev-snp-measure.py failed: {}", + crate::config::get_unified_command_output(&script_result) + ))); + } + Ok(String::from_utf8(script_result.stdout) + .map_err(|e| { + Error::FailedExecution(format!("Could not parse sev-snp-measure.py output: {e:?}")) + })? + .trim_end() + .to_string()) + } + + // This must produce the exact same params as the same operation on the daemon. + // Passing kernel params as String from the daemon leaves the install open for attacks. + fn kernel_params(&self) -> Result { + let mut ip_string = String::new(); + let mut public_ipv4 = false; + for ip in self.args.ips.iter() { + if ip.address.parse::()?.is_ipv4() { + public_ipv4 = true; + } + ip.gateway.parse::()?; + ip.mask.parse::()?; + ip_string += &format!( + "detee_net_eth{}={}_{}_{} ", + ip.nic_index, ip.address, ip.mask, ip.gateway + ); + } + if !public_ipv4 { + ip_string = "detee_net_eth0=10.0.2.15_24_10.0.2.2 ".to_string() + &ip_string; + } + let admin_key = format!("detee_admin={} ", Config::get_detee_wallet()?); + let hostname = format!("detee_name={}", self.hostname); + let params = format!("{}{}{}", ip_string, admin_key, hostname); + debug!("Calculated kernel params for {} to: {}", self.uuid, params); + Ok(params) + } +} + +pub fn execute( + measurement: String, + server_addr: String, + os_template: Option<(&str, &str)>, + log_file_name: &str, +) -> Result { + let parsed_addr = match server_addr.parse::() { + Ok(addr) => addr, + Err(_) => return Err(Error::InvalidVmAddress(server_addr)), + }; + let ssh_pubkey = Config::init_config().get_ssh_pubkey()?; + let ssh_args = super::VmSshArgs { + ip: parsed_addr.ip().to_string(), + port: parsed_addr.port().to_string(), + user: "root".to_string(), + key_path: ssh_pubkey.trim_end_matches(".pub").to_string(), + ..Default::default() + }; + eprintln!("Injecting disk encryption key into VM. This will take a minute. Do not interrupt."); + let (os_template_url, os_template_sha) = os_template.unwrap_or(("", "")); + let logs_path = Config::logs_dir()? + "/" + log_file_name; + log::info!("Logs will be saved to {}", logs_path); + let logs_file = std::fs::File::create(logs_path.clone())?; + let mut child_process = std::process::Command::new("detee-cli_injector.sh") + .env("SERVER_ADDR", server_addr) + .env("SSH_KEY_FILE", ssh_pubkey) + .env("DETEE_INSTALL_URL", os_template_url) + .env("DETEE_INSTALL_SHA", os_template_sha) + .env("MEASUREMENT", measurement) + .stdout(logs_file.try_clone()?) + .stderr(logs_file) + .spawn()?; + let status = child_process.wait()?; + if !status.success() { + log::error!( + "detee-cli_injector.sh exit code: {:?}! Check logs at: {}", + status.code(), + logs_path + ); + return Err(Error::InjectorError("Script execution failed".to_string())); + } + Ok(ssh_args) +} diff --git a/src/snp/mod.rs b/src/snp/mod.rs new file mode 100644 index 0000000..49952ca --- /dev/null +++ b/src/snp/mod.rs @@ -0,0 +1,468 @@ +pub mod deploy; +pub mod grpc; +mod injector; +pub mod update; + +use crate::{ + config::{self, Config}, + snp, +}; +use grpc::{block_on, brain}; +use lazy_static::lazy_static; +use serde::{Deserialize, Serialize}; +use tabled::Tabled; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Config(#[from] config::Error), + #[error("Could not find a contract with the ID {0}")] + VmContractNotFound(String), + #[error("Did not find a SNP node that matches your criteria")] + NoValidNodeFound, + #[error("Could not read yaml from disk: {0}")] + YamlNotFound(#[from] std::io::Error), + #[error("Failed to parse yaml update file: {0}")] + YamlFormat(#[from] serde_yaml::Error), + #[error("Brain returned the following error: {0}")] + Brain(#[from] grpc::Error), + #[error("Node returned the following error: {0}")] + Node(String), + #[error("Failed to obtain boot measurement for VM")] + NoMeasurement, + #[error("Failed to inject secrets: {0}")] + Injector(#[from] injector::Error), +} + +#[derive(Serialize, Default)] +pub struct VmSshArgs { + uuid: String, + hostname: String, + ip: String, + port: String, + user: String, + key_path: String, +} + +impl crate::HumanOutput for VmSshArgs { + fn human_cli_print(&self) { + println!("To SSH into {} (UUID: {}), run the following command:", self.hostname, self.uuid); + println!(" ssh -i {} -p {} {}@{}", self.key_path, self.port, self.user, self.ip); + } +} + +impl TryFrom for VmSshArgs { + type Error = Error; + + fn try_from(contract: grpc::brain::VmContract) -> Result { + let mut args = VmSshArgs { ..Default::default() }; + args.uuid = contract.uuid; + args.hostname = contract.hostname; + args.user = "root".to_string(); + args.key_path = + Config::init_config().get_ssh_pubkey()?.trim_end_matches(".pub").to_string(); + if contract.public_ipv4 != "" { + args.ip = contract.public_ipv4; + args.port = "22".to_string(); + } else { + args.port = contract.exposed_ports[0].to_string(); + log::info!( + "This VM does not have a public IP. Getting node IP for node {}", + contract.node_pubkey + ); + let node = block_on(snp::grpc::get_one_node(brain::VmNodeFilters { + node_pubkey: contract.node_pubkey.clone(), + ..Default::default() + }))?; + args.ip = node.ip; + } + Ok(args) + } +} + +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] +pub struct Dtrfs { + #[serde(default)] + name: String, + #[serde(default)] + vendor: String, + dtrfs_url: String, + dtrfs_sha: String, + kernel_url: String, + kernel_sha: String, +} + +impl Dtrfs { + pub fn print_dtrfs_list() -> Vec { + let mut dtrfs_vec = Vec::new(); + dtrfs_vec.push(DEFAULT_DTRFS.clone()); + dtrfs_vec.push(ALTERNATIVE_INIT[0].clone()); + dtrfs_vec.push(ALTERNATIVE_INIT[1].clone()); + dtrfs_vec + } + + fn load_from_file(config_path: &str) -> Result { + let content = std::fs::read_to_string(config_path)?; + Ok(serde_yaml::from_str(&content)?) + } +} + +impl super::HumanOutput for Vec { + fn human_cli_print(&self) { + print!("{}", serde_yaml::to_string(&self).unwrap()) + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct Distro { + name: String, + vendor: String, + template_url: String, + template_sha: String, +} + +impl super::HumanOutput for Vec { + fn human_cli_print(&self) { + print!("{}", serde_yaml::to_string(&self).unwrap()) + } +} + +impl Distro { + pub fn get_template_list() -> Vec { + let mut distro_vec = Vec::new(); + distro_vec.push(DEFAULT_ARCHLINUX.clone()); + distro_vec.push(DEFAULT_UBUNTU.clone()); + distro_vec.push(DEFAULT_FEDORA.clone()); + distro_vec.push(ALTERNATIVE_DISTROS[0].clone()); + distro_vec.push(ALTERNATIVE_DISTROS[1].clone()); + distro_vec.push(ALTERNATIVE_DISTROS[2].clone()); + distro_vec + } + + pub fn from_string(distro: &str) -> Distro { + match distro { + "arch" => DEFAULT_ARCHLINUX.clone(), + "ubuntu" => DEFAULT_UBUNTU.clone(), + "fedora" => DEFAULT_FEDORA.clone(), + _ => { + log::error!("Only arch, fedora and ubuntu are currently available"); + panic!(); + } + } + } +} + +#[derive(Tabled, Debug, Serialize, Deserialize)] +pub struct VmContract { + #[tabled(rename = "Location")] + pub location: String, + #[tabled(rename = "UUID")] + pub uuid: String, + pub hostname: String, + #[tabled(rename = "Cores")] + pub vcpus: u32, + #[tabled(rename = "Mem (MB)")] + pub mem: u32, + #[tabled(rename = "Disk")] + pub disk: u32, + #[tabled(rename = "LP/h")] + pub cost_h: f64, + #[tabled(rename = "time left", display_with = "display_mins")] + pub time_left: u64, +} + +impl crate::HumanOutput for Vec { + fn human_cli_print(&self) { + let style = tabled::settings::Style::rounded(); + let mut table = tabled::Table::new(self); + table.with(style); + println!("{table}"); + } +} + +fn display_mins(minutes: &u64) -> String { + let mins = minutes % 60; + let hours = minutes / 60; + + format!("{hours}h {mins}m") +} + +impl From for VmContract { + fn from(brain_contract: brain::VmContract) -> Self { + let node_pubkey = brain_contract.node_pubkey.clone(); + let location = match block_on(snp::grpc::get_one_node(brain::VmNodeFilters { + node_pubkey: node_pubkey.clone(), + ..Default::default() + })) { + Ok(node) => format!("{}, {} ({})", node.city, node.region, node.country), + Err(e) => { + log::warn!("Could not get information about node {node_pubkey} fram brain: {e:?}"); + String::new() + } + }; + + Self { + uuid: brain_contract.uuid, + hostname: brain_contract.hostname, + vcpus: brain_contract.vcpus, + mem: brain_contract.memory_mb, + disk: brain_contract.disk_size_gb, + location, + cost_h: (brain_contract.nano_per_minute * 60) as f64 / 1_000_000_000.0, + time_left: brain_contract.locked_nano / brain_contract.nano_per_minute, + } + } +} + +#[derive(Tabled, Debug, Serialize, Deserialize)] +pub struct TabledVmNode { + #[tabled(rename = "Operator")] + pub operator: String, + #[tabled(rename = "City, Region, Country")] + pub location: String, + #[tabled(rename = "IP")] + pub public_ip: String, + #[tabled(rename = "Price per unit")] + pub price: String, + #[tabled(rename = "Reports")] + pub reports: usize, +} + +impl From for TabledVmNode { + fn from(brain_node: brain::VmNodeListResp) -> Self { + Self { + operator: brain_node.operator, + location: brain_node.city + ", " + &brain_node.region + ", " + &brain_node.country, + public_ip: brain_node.ip, + price: format!("{} nanoLP/min", brain_node.price), + reports: brain_node.reports.len(), + } + } +} + +pub fn ssh(uuid: &str, just_print: bool) -> Result { + log::info!("Getting VM information about {uuid} from brain..."); + let req = brain::ListVmContractsReq { + wallet: Config::get_detee_wallet()?, + uuid: uuid.to_string(), + ..Default::default() + }; + let contracts = block_on(snp::grpc::list_contracts(req))?; + if contracts.len() == 0 { + return Err(Error::VmContractNotFound(uuid.to_string())); + } + let args: VmSshArgs = contracts[0].clone().try_into()?; + if just_print { + return Ok(args); + } + eprintln!( + "Running SSH command: ssh -i {} -p {} {}@{}", + args.key_path, args.port, args.user, args.ip + ); + use std::os::unix::process::CommandExt; + let e = std::process::Command::new("ssh") + .arg("-i") + .arg(args.key_path) + .arg("-p") + .arg(args.port) + .arg(format!("{}@{}", args.user, args.ip)) + .stdin(std::process::Stdio::inherit()) + .stdout(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::inherit()) + .exec(); + + println!("Error: Failed to execute ssh process: {e}"); + std::process::exit(2); +} + +pub fn get_one_contract(uuid: &str) -> Result { + log::debug!("Getting contract {uuid} from brain..."); + let req = brain::ListVmContractsReq { + wallet: Config::get_detee_wallet()?, + uuid: uuid.to_string(), + ..Default::default() + }; + let contracts = block_on(snp::grpc::list_contracts(req))?; + if contracts.is_empty() { + return Err(Error::VmContractNotFound(uuid.to_string())); + } + Ok(contracts[0].clone()) +} + +pub fn get_node_by_contract(uuid: &str) -> Result { + let contract = get_one_contract(uuid)?; + Ok(block_on(snp::grpc::get_one_node(brain::VmNodeFilters { + node_pubkey: contract.node_pubkey, + ..Default::default() + }))?) +} + +pub fn delete_contract(uuid: &str) -> Result<(), Error> { + Ok(block_on(snp::grpc::delete_vm(uuid))?) +} + +pub fn list_contracts(as_operator: bool) -> Result, Error> { + let req = brain::ListVmContractsReq { + wallet: Config::get_detee_wallet()?, + as_operator, + ..Default::default() + }; + let contracts: Vec = + block_on(grpc::list_contracts(req))?.into_iter().map(|n| n.into()).collect(); + let _ = write_uuid_list(&contracts); + Ok(contracts) +} + +fn write_uuid_list(contracts: &Vec) -> Result<(), Error> { + let vm_uuid_list_path = Config::vm_uuid_list_path()?; + let mut file = std::fs::File::create(vm_uuid_list_path)?; + let output: String = contracts + .iter() + .map(|vm| format!("{}\t{}", vm.uuid, vm.hostname).to_string()) + .collect::>() + .join("\n"); + let output = output + "\n"; + std::io::Write::write_all(&mut file, output.as_bytes())?; + Ok(()) +} + +pub fn append_uuid_list(uuid: &str, hostname: &str) -> Result<(), Error> { + use std::{fs::OpenOptions, io::prelude::*}; + let mut file = OpenOptions::new().write(true).append(true).open(Config::vm_uuid_list_path()?)?; + writeln!(file, "{uuid}\t{hostname}")?; + Ok(()) +} + +impl super::HumanOutput for Vec { + fn human_cli_print(&self) { + let nodes: Vec = self.iter().map(|n| n.clone().into()).collect(); + let style = tabled::settings::Style::rounded(); + let mut table = tabled::Table::new(nodes); + table.with(style); + println!("{table}"); + } +} + +pub fn print_nodes() -> Result, Error> { + log::debug!("This will support flags in the future, but we have only one node atm."); + let req = brain::VmNodeFilters { ..Default::default() }; + Ok(block_on(grpc::get_node_list(req))?) +} + +pub fn inspect_node(ip: String) -> Result { + let req = brain::VmNodeFilters { ip, ..Default::default() }; + Ok(block_on(grpc::get_one_node(req))?) +} + +pub fn report_node( + node_pubkey: String, + contract: String, + reason: String, +) -> Result { + block_on(grpc::report_node(node_pubkey, contract, reason))?; + Ok(super::SimpleOutput::from("The node got reported.")) +} + +pub fn calculate_nanolp( + vcpus: u32, + memory_mb: u32, + disk_size_gb: u32, + public_ipv4: bool, + hours: u32, + node_price: u64, +) -> u64 { + // this calculation needs to match the calculation of the network + let total_units = (vcpus as u64 * 10) + + ((memory_mb + 256) as u64 / 200) + + (disk_size_gb as u64 / 10) + + (public_ipv4 as u64 * 10); + let locked_nano = hours as u64 * 60 * total_units * node_price; + eprint!( + "Node price: {}/unit/minute. Total Units for hardware requested: {}. ", + node_price as f64 / 1_000_000_000.0, + total_units, + ); + eprint!( + "Locking {} LP (offering the VM for {} hours).\n", + locked_nano as f64 / 1_000_000_000.0, + hours + ); + locked_nano +} + +lazy_static! { + static ref DEFAULT_DTRFS: Dtrfs = Dtrfs { + name: "dtrfs-6.13.6-arch1-1".to_string(), + vendor: "ghe0".to_string(), + dtrfs_url: "http://registry.detee.ltd/detee-archtop-6.13.6-arch1-1.cpio.gz".to_string(), + dtrfs_sha: "de48048fb42fe4054611f14e51ce175ca90645734fe41349642f036b8bca8fcd".to_string(), + kernel_url: "http://registry.detee.ltd/vmlinuz-linux-6.13.6-arch1-1".to_string(), + kernel_sha: "7efaca6c348cd4136afe3ece0beec346da713029347a0d4e71e12a0b91570de7".to_string() + }; + static ref DEFAULT_ARCHLINUX: Distro = Distro { + name: "archlinux_2025-02-21".to_string(), + vendor: "gheorghe".to_string(), + template_url: "http://registry.detee.ltd/detee_arch_2025-02-21.fsa".to_string(), + template_sha: "257edbf1e3b949b895c422befc8890c85dfae1ad3d35661010c9aaa173ba9fc4" + .to_string() + }; + static ref DEFAULT_UBUNTU: Distro = Distro { + name: "ubuntu_2025-02-28".to_string(), + vendor: "gheorghe".to_string(), + template_url: "http://registry.detee.ltd/detee_ubuntu_2025-02-28.fsa".to_string(), + template_sha: "faa8bd38d02ca9b6ee69d7f5128ed9ccab42bdbfa69f688b9947e8e5c9e5d133" + .to_string() + }; + static ref DEFAULT_FEDORA: Distro = Distro { + name: "fedora_2025-02-21".to_string(), + vendor: "gheorghe".to_string(), + template_url: "http://registry.detee.ltd/detee_fedora_2025-02-21.fsa".to_string(), + template_sha: "c0fdd08d465939077ef8ed746903005fc190af12cdf70917cc8c6f872da85777" + .to_string() + }; + static ref ALTERNATIVE_INIT: Vec = vec![ + Dtrfs { + name: "dtrfs-6.13.6-arch1-1".to_string(), + vendor: "ghe0".to_string(), + dtrfs_url: "http://registry.detee.ltd/detee-archtop-6.13.6-arch1-1.cpio.gz".to_string(), + dtrfs_sha: "83675cf2a27db526ec0705daf2606674778759fb33cdb8b1dfc4ddd623608806" + .to_string(), + kernel_url: "http://registry.detee.ltd/vmlinuz-linux-6.13.6-arch1-1".to_string(), + kernel_sha: "7efaca6c348cd4136afe3ece0beec346da713029347a0d4e71e12a0b91570de7" + .to_string() + }, + Dtrfs { + name: "dtrfs-6.13.4-arch1-1".to_string(), + vendor: "ghe0".to_string(), + dtrfs_url: "http://registry.detee.ltd/detee-archtop-6.13.4-arch1-1.cpio.gz".to_string(), + dtrfs_sha: "3f6b3e5740f249eedfb2f7248c521a551be8b2676f7fcb040f3f3bc840a5004b" + .to_string(), + kernel_url: "http://registry.detee.ltd/vmlinuz-linux-6.13.4-arch1-1".to_string(), + kernel_sha: "3ec4fc5aa5729f515967ec71be4a851622785c0080f7191b1b07717149840151" + .to_string() + }, + ]; + static ref ALTERNATIVE_DISTROS: Vec = vec![ + Distro { + name: "archlinux_2025-01-27".to_string(), + vendor: "gheorghe".to_string(), + template_url: "http://registry.detee.ltd/detee_arch_2025-01-27.fsa".to_string(), + template_sha: "c8cc8ef611380c2d1fbab36e44ccfd8d666e344c7aaefe763f7dd6136b672c97" + .to_string() + }, + Distro { + name: "ubuntu_2025-02-21".to_string(), + vendor: "gheorghe".to_string(), + template_url: "http://registry.detee.ltd/detee_ubuntu_2025-02-21.fsa".to_string(), + template_sha: "180e43c46494c8b5cf2b19067995755ade1bbd80396e1fd5e1c4b164ed2fe8cf" + .to_string() + }, + Distro { + name: "fedora_2025-01-28".to_string(), + vendor: "gheorghe".to_string(), + template_url: "http://registry.detee.ltd/detee_fedora_2025-01-28.fsa".to_string(), + template_sha: "68c5be46d668a12e8ff78692843a922315bd5cd9c2bb53accf2685ec3be1fa31" + .to_string() + } + ]; +} diff --git a/src/snp/update.rs b/src/snp/update.rs new file mode 100644 index 0000000..f50aa9d --- /dev/null +++ b/src/snp/update.rs @@ -0,0 +1,98 @@ +use super::{ + grpc::{self, block_on, brain}, + injector, Dtrfs, Error, +}; +use crate::config::Config; +use log::{debug, info}; + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Request { + vcpus: u32, + memory_mb: u32, + disk_size_gb: u32, + dtrfs: Option, +} + +impl Request { + pub fn process_request( + uuid: &str, + vcpus: u32, + memory_mb: u32, + disk_size_gb: u32, + dtrfs: &str, + ) -> Result<(), Error> { + let dtrfs = match dtrfs { + "" => None, + "latest" => Some(super::DEFAULT_DTRFS.clone()), + path => { + log::info!("Loading DTRFS from path: {path}"); + Some(Dtrfs::load_from_file(path)?) + } + }; + let req = Self { vcpus, memory_mb, disk_size_gb, dtrfs }; + if req == Self::default() { + log::info!("Skipping hardware upgrade (no arguments specified)."); + return Ok(()); + } + log::info!("Starting VM updated based on req: {req:#?}"); + req.update(uuid) + } + + fn update(&self, uuid: &str) -> Result<(), Error> { + info!("Starting the process of updating the VM. {self:?}"); + let update_vm_resp = self.send_update_vm_request(uuid)?; + debug!("The response for Update VM is: {update_vm_resp:#?}"); + if !update_vm_resp.error.is_empty() { + return Err(Error::Node(update_vm_resp.error)); + } + + eprintln!("The node accepted the hardware modifications for the VM."); + let current_contract = block_on(grpc::get_contract_by_uuid(uuid))?; + debug!("Got the current contract for the VM after update. {current_contract:#?}"); + + let args = update_vm_resp.args.ok_or(Error::NoMeasurement)?; + let measurement_args = injector::Args { + uuid: update_vm_resp.uuid, + hostname: current_contract.hostname.clone(), + vcpus: current_contract.vcpus, + kernel: current_contract.kernel_sha, + initrd: current_contract.dtrfs_sha, + args: args.clone(), + }; + let measurement = measurement_args.get_measurement()?; + + if self.vcpus != 0 || self.dtrfs.is_some() { + injector::execute(measurement, args.dtrfs_api_endpoint, None, uuid)?; + } else { + println!("vCPUs and kernel did not get modified. Secret injection is not required."); + } + Ok(()) + } + + // returns node IP and data regarding the new VM + fn send_update_vm_request(&self, uuid: &str) -> Result { + let (kernel_url, kernel_sha, dtrfs_url, dtrfs_sha) = match self.dtrfs.clone() { + Some(dtrfs) => (dtrfs.kernel_url, dtrfs.kernel_sha, dtrfs.dtrfs_url, dtrfs.dtrfs_sha), + None => (String::new(), String::new(), String::new(), String::new()), + }; + Ok(block_on(grpc::update_vm(brain::UpdateVmReq { + uuid: uuid.to_string(), + admin_pubkey: Config::get_detee_wallet()?, + disk_size_gb: self.disk_size_gb, + vcpus: self.vcpus, + memory_mb: self.memory_mb, + kernel_url, + kernel_sha, + dtrfs_url, + dtrfs_sha, + }))?) + } +} + +pub fn expand_vm_hours(uuid: &str, hours: u32) -> Result<(), Error> { + let contract = super::get_one_contract(uuid)?; + // vcpus: u32, memory_mb: u32, disk_size_gb: u32, public_ipv4: bool, hours: u32, node_price: + let locked_nano = contract.nano_per_minute * 60 * (hours as u64); + block_on(grpc::extend_vm(uuid.to_string(), Config::get_detee_wallet()?, locked_nano))?; + Ok(()) +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..7829371 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,114 @@ +use crate::config::Config; +use crate::constants::HRATLS_APP_PORT; +use crate::sgx::grpc_brain::list_apps; +use serde::{Deserialize, Serialize}; +use tonic::metadata::errors::InvalidMetadataValue; +use tonic::metadata::AsciiMetadataValue; +use tonic::Request; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + ConfigError(#[from] crate::config::Error), + #[error(transparent)] + InternalError(#[from] InvalidMetadataValue), + #[error(transparent)] + Reqwest(#[from] reqwest::Error), + #[error(transparent)] + Serde(#[from] serde_yaml::Error), + #[error("{0}")] + PublicPackage(std::string::String), +} + +pub fn block_on(future: F) -> F::Output +where + F: std::future::Future, +{ + tokio::runtime::Runtime::new().unwrap().block_on(future) +} + +pub fn sign_request(req: T) -> Result, Error> { + let pubkey = Config::get_detee_wallet()?; + let timestamp = chrono::Utc::now().to_rfc3339(); + let signature = Config::try_sign_message(&format!("{timestamp}{req:?}"))?; + let timestamp: AsciiMetadataValue = timestamp.parse()?; + let pubkey: AsciiMetadataValue = pubkey.parse()?; + let signature: AsciiMetadataValue = signature.parse()?; + let mut req = Request::new(req); + req.metadata_mut().insert("timestamp", timestamp); + req.metadata_mut().insert("pubkey", pubkey); + req.metadata_mut().insert("request-signature", signature); + Ok(req) +} + +pub async fn hratls_url_and_mr_enclave_from_app_id(app_id: &str) -> (String, Option<[u8; 32]>) { + let app_contracts = list_apps().await.expect("Could not get App contracts"); + let app_contract = app_contracts.iter().find(|contract| contract.uuid == app_id); + if app_contract.is_none() { + eprintln!("Could not find App contract with ID: {}", app_id); + std::process::exit(1); + } + let app_contract = app_contract.unwrap(); + let mr_enclave = app_contract + .public_package_mr_enclave + .clone() + .filter(|vec| vec.len() == 32) + .and_then(|vec| vec.try_into().ok()); + + let public_ip = app_contract.public_ipv4.clone(); + let dtpm_port = app_contract + .mapped_ports + .iter() + .find(|port| port.app_port == HRATLS_APP_PORT) + .unwrap() + .host_port; + + (format!("https://{public_ip}:{dtpm_port}"), mr_enclave) +} + +#[derive(Serialize, Deserialize, Debug)] +struct PublicIndex { + packages: Vec, +} +#[derive(Serialize, Deserialize, Debug)] +struct PackageElement { + package_url: String, + mr_enclave: [u8; 32], +} + +pub async fn mr_enclave_from_public_registry(package_url: &str) -> Result<[u8; 32], Error> { + let public_index = + reqwest::get("https://registry.detee.ltd/sgx/public_index.yaml").await?.text().await?; + + let index = serde_yaml::from_str::(&public_index)?; + + let pub_package_mr_enclave = index + .packages + .iter() + .find(|package| package.package_url == package_url) + .ok_or(Error::PublicPackage("mr_enclave not found for this public package".to_string()))? + .mr_enclave; + + Ok(pub_package_mr_enclave) +} + +pub fn calculate_nanolp_for_app( + vcpus: u32, + memory_mb: u32, + disk_size_mb: u32, + hours: u64, + node_price: u64, +) -> u64 { + // this calculation needs to match the calculation of the network + let total_units = + (vcpus as f64 * 5f64) + (memory_mb as f64 / 200f64) + (disk_size_mb as f64 / 10000f64); + let locked_nano = (hours as f64 * 60f64 * total_units * node_price as f64) as u64; + println!( + "Node price: {}/unit/minute. Total Units for hardware requested: {:.4}. Locking {} LP (offering the App for {} hours).", + node_price as f64 / 1_000_000_000.0, + total_units, + locked_nano as f64 / 1_000_000_000.0, + hours + ); + locked_nano +} diff --git a/vm.proto b/vm.proto new file mode 100644 index 0000000..f0f4d2e --- /dev/null +++ b/vm.proto @@ -0,0 +1,271 @@ +syntax = "proto3"; +package vm_proto; + +message Empty { +} + +message Pubkey { + string pubkey = 1; +} + +message AccountBalance { + uint64 balance = 1; + uint64 tmp_locked = 2; +} + +message VmContract { + string uuid = 1; + string hostname = 2; + string admin_pubkey = 3; + string node_pubkey = 4; + repeated uint32 exposed_ports = 5; + string public_ipv4 = 6; + string public_ipv6 = 7; + uint32 disk_size_gb = 8; + uint32 vcpus = 9; + uint32 memory_mb = 10; + string kernel_sha = 11; + string dtrfs_sha = 12; + string created_at = 13; + string updated_at = 14; + // total nanoLP cost per minute (for all units) + uint64 nano_per_minute = 15; + uint64 locked_nano = 16; + string collected_at = 17; +} + +message MeasurementArgs { + // this will be IP:Port of the dtrfs API + // actually not a measurement arg, but needed for the injector + string dtrfs_api_endpoint = 1; + repeated uint32 exposed_ports = 2; + string ovmf_hash = 5; + // This is needed to allow the CLI to build the kernel params from known data. + // The CLI will use the kernel params to get the measurement. + repeated MeasurementIP ips = 6; +} + +message MeasurementIP { + uint32 nic_index = 1; + string address = 2; + string mask = 3; + string gateway = 4; +} + +// This should also include a block hash or similar, for auth +message RegisterVmNodeReq { + string node_pubkey = 1; + string operator_wallet = 2; + string main_ip = 3; + string country = 4; + string region = 5; + string city = 6; + // nanoLP per unit per minute + uint64 price = 7; +} + +message VmNodeResources { + string node_pubkey = 1; + uint32 avail_ports = 2; + uint32 avail_ipv4 = 3; + uint32 avail_ipv6 = 4; + uint32 avail_vcpus = 5; + uint32 avail_memory_mb = 6; + uint32 avail_storage_gb = 7; + uint32 max_ports_per_vm = 8; +} + +message NewVmReq { + string uuid = 1; + string hostname = 2; + string admin_pubkey = 3; + string node_pubkey = 4; + repeated uint32 extra_ports = 5; + bool public_ipv4 = 6; + bool public_ipv6 = 7; + uint32 disk_size_gb = 8; + uint32 vcpus = 9; + uint32 memory_mb = 10; + string kernel_url = 11; + string kernel_sha = 12; + string dtrfs_url = 13; + string dtrfs_sha = 14; + uint64 price_per_unit = 15; + uint64 locked_nano = 16; +} + +message NewVmResp { + string uuid = 1; + string error = 2; + MeasurementArgs args = 3; +} + +message UpdateVmReq { + string uuid = 1; + string admin_pubkey = 2; + uint32 disk_size_gb = 3; + uint32 vcpus = 4; + uint32 memory_mb = 5; + string kernel_url = 6; + string kernel_sha = 7; + string dtrfs_url = 8; + string dtrfs_sha = 9; +} + +message UpdateVmResp { + string uuid = 1; + string error = 2; + MeasurementArgs args = 3; +} + +message DeleteVmReq { + string uuid = 1; + string admin_pubkey = 2; +} + +message BrainVmMessage { + oneof Msg { + NewVmReq new_vm_req = 1; + UpdateVmReq update_vm_req = 2; + DeleteVmReq delete_vm = 3; + } +} + +message DaemonStreamAuth { + string timestamp = 1; + string pubkey = 2; + repeated string contracts = 3; + string signature = 4; +} + +message VmDaemonMessage { + oneof Msg { + DaemonStreamAuth auth = 1; + NewVmResp new_vm_resp = 2; + UpdateVmResp update_vm_resp = 3; + VmNodeResources vm_node_resources = 4; + } +} + +service BrainVmDaemon { + rpc RegisterVmNode (RegisterVmNodeReq) returns (stream VmContract); + rpc BrainMessages (DaemonStreamAuth) returns (stream BrainVmMessage); + rpc DaemonMessages (stream VmDaemonMessage) returns (Empty); +} + +message ListVmContractsReq { + string wallet = 1; + bool as_operator = 2; + string uuid = 3; +} + +message VmNodeFilters { + uint32 free_ports = 1; + bool offers_ipv4 = 2; + bool offers_ipv6 = 3; + uint32 vcpus = 4; + uint32 memory_mb = 5; + uint32 storage_gb = 6; + string country = 7; + string region = 8; + string city = 9; + string ip = 10; + string node_pubkey = 11; +} + +message VmNodeListResp { + string operator = 1; + string node_pubkey = 2; + string country = 3; + string region = 4; + string city = 5; + string ip = 6; // required for latency test + repeated string reports = 7; // TODO: this will become an enum + uint64 price = 8; // nanoLP per unit per minute +} + +message ExtendVmReq { + string uuid = 1; + string admin_pubkey = 2; + uint64 locked_nano = 3; +} + +message AirdropReq { + string pubkey = 1; + uint64 tokens = 2; +} + +message SlashReq { + string pubkey = 1; + uint64 tokens = 2; +} + +message Account { + string pubkey = 1; + uint64 balance = 2; + uint64 tmp_locked = 3; +} + +message RegOperatorReq { + string pubkey = 1; + uint64 escrow = 2; + string email = 3; +} + +message ListOperatorsResp { + string pubkey = 1; + uint64 escrow = 2; + string email = 3; + uint64 app_nodes = 4; + uint64 vm_nodes = 5; + uint64 reports = 6; +} + +message InspectOperatorResp { + ListOperatorsResp operator = 1; + repeated VmNodeListResp nodes = 2; +} + +message ReportNodeReq { + string admin_pubkey = 1; + string node_pubkey = 2; + string contract = 3; + string reason = 4; +} + +message KickReq { + string operator_wallet = 1; + string contract_uuid = 2; + string reason = 3; +} + +message BanUserReq { + string operator_wallet = 1; + string user_wallet = 2; +} + +message KickResp { + uint64 nano_lp = 1; +} + +service BrainCli { + rpc GetBalance (Pubkey) returns (AccountBalance); + rpc NewVm (NewVmReq) returns (NewVmResp); + rpc ListVmContracts (ListVmContractsReq) returns (stream VmContract); + rpc ListVmNodes (VmNodeFilters) returns (stream VmNodeListResp); + rpc GetOneVmNode (VmNodeFilters) returns (VmNodeListResp); + rpc DeleteVm (DeleteVmReq) returns (Empty); + rpc UpdateVm (UpdateVmReq) returns (UpdateVmResp); + rpc ExtendVm (ExtendVmReq) returns (Empty); + rpc ReportNode (ReportNodeReq) returns (Empty); + rpc ListOperators (Empty) returns (stream ListOperatorsResp); + rpc InspectOperator (Pubkey) returns (InspectOperatorResp); + rpc RegisterOperator (RegOperatorReq) returns (Empty); + rpc KickContract (KickReq) returns (KickResp); + rpc BanUser (BanUserReq) returns (Empty); + // admin commands + rpc Airdrop (AirdropReq) returns (Empty); + rpc Slash (SlashReq) returns (Empty); + rpc ListAllVmContracts (Empty) returns (stream VmContract); + rpc ListAccounts (Empty) returns (stream Account); +}