From 80f74deff63d29eff4a732c9109585b36c616923 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 28 Nov 2023 13:42:16 -0800 Subject: [PATCH 01/58] feat: canonical numbering work around. --- canonical/.cargo/config.toml | 3 + canonical/Cargo.lock | 496 +++++++++++++++++- canonical/Cargo.toml | 19 +- canonical/README.md | 66 +++ canonical/canonical-block-executor/Cargo.toml | 7 + .../src/canonical_block_executor/mod.rs | 8 +- .../sui_block_executor/mod.rs | 274 ++++++++-- canonical/canonical-block-executor/src/lib.rs | 1 + canonical/rsc/canonical_shared_resolver.png | Bin 0 -> 108418 bytes canonical/rsc/sui_block_executor.png | Bin 0 -> 80085 bytes movement-sdk/Cargo.lock | 63 +-- movement-sdk/Cargo.toml | 4 +- .../movement-sdk-avalanche/Cargo.toml | 4 +- .../src/data_availability/block.rs | 167 ++++++ .../data_availability_layer.rs | 97 ++++ .../src/data_availability/mod.rs | 84 +-- movement-sdk/movement-sdk/src/lib.rs | 16 +- 17 files changed, 1108 insertions(+), 201 deletions(-) create mode 100644 canonical/README.md create mode 100644 canonical/rsc/canonical_shared_resolver.png create mode 100644 canonical/rsc/sui_block_executor.png create mode 100644 movement-sdk/movement-sdk-avalanche/src/data_availability/block.rs create mode 100644 movement-sdk/movement-sdk-avalanche/src/data_availability/data_availability_layer.rs diff --git a/canonical/.cargo/config.toml b/canonical/.cargo/config.toml index f93b5556e..6b23d9edc 100644 --- a/canonical/.cargo/config.toml +++ b/canonical/.cargo/config.toml @@ -31,3 +31,6 @@ rustflags = [ "-C", "link-arg=/STACK:8000000" # Set stack to 8 MB ] + +[target.aarch64-apple-darwin] +rustflags = ["-C", "link-arg=-fuse-ld=/opt/homebrew/opt/llvm/bin/ld64.lld"] \ No newline at end of file diff --git a/canonical/Cargo.lock b/canonical/Cargo.lock index 83fd646a5..ecdd33339 100644 --- a/canonical/Cargo.lock +++ b/canonical/Cargo.lock @@ -147,7 +147,7 @@ dependencies = [ "rand 0.8.5", "rcgen 0.9.3", "ring 0.16.20", - "rustls", + "rustls 0.21.8", "rustls-webpki", "serde 1.0.190", "serde_json", @@ -1097,6 +1097,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -1135,6 +1144,12 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "async_once" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ce4f10ea3abcd6617873bae9f91d1c5332b4a778bd9ce34d0cd517474c1de82" + [[package]] name = "atomic_float" version = "0.1.0" @@ -1309,10 +1324,10 @@ dependencies = [ "http-body", "hyper", "pin-project-lite", - "rustls", + "rustls 0.21.8", "rustls-pemfile", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tower-service", ] @@ -1415,6 +1430,15 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde 1.0.190", +] + [[package]] name = "better_any" version = "0.1.1" @@ -1765,6 +1789,44 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "cached" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc2fafddf188d13788e7099295a59b99e99b2148ab2195cae454e754cc099925" +dependencies = [ + "async-trait", + "async_once", + "cached_proc_macro", + "cached_proc_macro_types", + "futures 0.3.28", + "hashbrown 0.13.2", + "instant", + "lazy_static 1.4.0", + "once_cell", + "thiserror", + "tokio", +] + +[[package]] +name = "cached_proc_macro" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10ca87c81aaa3a949dbbe2b5e6c2c45dbc94ba4897e45ea31ff9ec5087be3dc" +dependencies = [ + "cached_proc_macro_types", + "darling 0.14.4", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "cached_proc_macro_types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" + [[package]] name = "camino" version = "1.1.6" @@ -1782,6 +1844,7 @@ dependencies = [ "bcs 0.1.6", "bytes", "chrono", + "ctor", "derivative", "dirs 5.0.1", "env_logger", @@ -1798,9 +1861,14 @@ dependencies = [ "serde_with 2.3.3", "sui-adapter-latest", "sui-core", + "sui-swarm-config", + "sui-test-transaction-builder", "sui-types", + "tempfile", "tokio", "tonic 0.8.3", + "tracing", + "tracing-subscriber", ] [[package]] @@ -1896,7 +1964,7 @@ dependencies = [ "random-manager", "rcgen 0.10.0", "rsa 0.9.3", - "rustls", + "rustls 0.21.8", "rustls-pemfile", "x509-parser 0.15.1", ] @@ -2386,6 +2454,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "ctor" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e366bff8cd32dd8754b0991fb66b279dc48f598c3a18914852a6673deef583" +dependencies = [ + "quote 1.0.33", + "syn 2.0.39", +] + [[package]] name = "ctr" version = "0.9.2" @@ -2588,6 +2666,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive-syn-parse" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "derive_builder" version = "0.12.0" @@ -3881,6 +3970,22 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "log", + "rustls 0.20.9", + "rustls-native-certs", + "tokio", + "tokio-rustls 0.23.4", + "webpki-roots 0.22.6", +] + [[package]] name = "hyper-rustls" version = "0.24.2" @@ -3891,10 +3996,10 @@ dependencies = [ "http", "hyper", "log", - "rustls", + "rustls 0.21.8", "rustls-native-certs", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", ] [[package]] @@ -4343,6 +4448,142 @@ dependencies = [ "serde 1.0.190", ] +[[package]] +name = "jsonrpsee" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-proc-macros", + "jsonrpsee-server", + "jsonrpsee-types", + "jsonrpsee-ws-client", + "tracing", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "futures-util", + "http", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project", + "rustls-native-certs", + "soketto", + "thiserror", + "tokio", + "tokio-rustls 0.23.4", + "tokio-util 0.7.10", + "tracing", + "webpki-roots 0.22.6", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "anyhow", + "arrayvec 0.7.4", + "async-lock", + "async-trait", + "beef", + "futures-channel", + "futures-timer", + "futures-util", + "globset", + "hyper", + "jsonrpsee-types", + "parking_lot 0.12.1", + "rand 0.8.5", + "rustc-hash", + "serde 1.0.190", + "serde_json", + "soketto", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "async-trait", + "hyper", + "hyper-rustls 0.23.2", + "jsonrpsee-core", + "jsonrpsee-types", + "rustc-hash", + "serde 1.0.190", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 1.1.3", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "futures-channel", + "futures-util", + "http", + "hyper", + "jsonrpsee-core", + "jsonrpsee-types", + "serde 1.0.190", + "serde_json", + "soketto", + "tokio", + "tokio-stream", + "tokio-util 0.7.10", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "anyhow", + "beef", + "serde 1.0.190", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "http", + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", +] + [[package]] name = "k256" version = "0.11.6" @@ -7752,7 +7993,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls", + "rustls 0.21.8", "thiserror", "tokio", "tracing", @@ -7768,7 +8009,7 @@ dependencies = [ "rand 0.8.5", "ring 0.16.20", "rustc-hash", - "rustls", + "rustls 0.21.8", "slab", "thiserror", "tinyvec", @@ -8117,7 +8358,7 @@ dependencies = [ "http", "http-body", "hyper", - "hyper-rustls", + "hyper-rustls 0.24.2", "hyper-tls", "ipnet", "js-sys", @@ -8127,7 +8368,7 @@ dependencies = [ "once_cell", "percent-encoding 2.3.0", "pin-project-lite", - "rustls", + "rustls 0.21.8", "rustls-pemfile", "serde 1.0.190", "serde_json", @@ -8135,7 +8376,7 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", - "tokio-rustls", + "tokio-rustls 0.24.1", "tokio-util 0.7.10", "tower-service", "url 2.4.1", @@ -8143,7 +8384,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots", + "webpki-roots 0.25.2", "winreg", ] @@ -8385,6 +8626,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rustls" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" +dependencies = [ + "log", + "ring 0.16.20", + "sct", + "webpki", +] + [[package]] name = "rustls" version = "0.21.8" @@ -8865,6 +9118,19 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha1" version = "0.10.6" @@ -9120,6 +9386,22 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "soketto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" +dependencies = [ + "base64 0.13.1", + "bytes", + "futures 0.3.28", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha-1", +] + [[package]] name = "spin" version = "0.5.2" @@ -9587,6 +9869,53 @@ dependencies = [ "workspace-hack", ] +[[package]] +name = "sui-json-rpc" +version = "0.0.0-canonical-sui" +dependencies = [ + "anyhow", + "arc-swap", + "async-trait", + "axum", + "bcs 0.1.6", + "cached", + "eyre", + "fastcrypto", + "futures 0.3.28", + "hyper", + "itertools 0.10.5", + "jsonrpsee", + "linked-hash-map", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-package 0.1.0-canonical-sui", + "mysten-metrics", + "once_cell", + "prometheus", + "serde 1.0.190", + "serde_json", + "shared-crypto", + "signature 1.6.4", + "sui-core", + "sui-json", + "sui-json-rpc-types", + "sui-open-rpc", + "sui-open-rpc-macros", + "sui-protocol-config", + "sui-storage", + "sui-transaction-builder", + "sui-types", + "tap", + "thiserror", + "tokio", + "tower", + "tower-http", + "tracing", + "typed-store-error", + "workspace-hack", +] + [[package]] name = "sui-json-rpc-types" version = "0.0.0-canonical-sui" @@ -9757,6 +10086,31 @@ dependencies = [ "workspace-hack", ] +[[package]] +name = "sui-open-rpc" +version = "1.15.0-canonical-sui" +dependencies = [ + "bcs 0.1.6", + "schemars", + "serde 1.0.190", + "serde_json", + "versions", + "workspace-hack", +] + +[[package]] +name = "sui-open-rpc-macros" +version = "0.1.0-canonical-sui" +dependencies = [ + "derive-syn-parse", + "itertools 0.10.5", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", + "unescape", + "workspace-hack", +] + [[package]] name = "sui-proc-macros" version = "0.7.0-canonical-sui" @@ -9793,6 +10147,38 @@ dependencies = [ "workspace-hack", ] +[[package]] +name = "sui-sdk" +version = "1.15.0-canonical-sui" +dependencies = [ + "anyhow", + "async-trait", + "bcs 0.1.6", + "clap 4.4.7", + "colored", + "fastcrypto", + "futures 0.3.28", + "futures-core", + "jsonrpsee", + "move-core-types 0.0.4-canonical-sui", + "reqwest", + "serde 1.0.190", + "serde_json", + "serde_with 2.3.3", + "shared-crypto", + "sui-config", + "sui-json", + "sui-json-rpc", + "sui-json-rpc-types", + "sui-keys", + "sui-transaction-builder", + "sui-types", + "thiserror", + "tokio", + "tracing", + "workspace-hack", +] + [[package]] name = "sui-simulator" version = "0.7.0-canonical-sui" @@ -9832,7 +10218,7 @@ dependencies = [ "fastcrypto", "futures 0.3.28", "hyper", - "hyper-rustls", + "hyper-rustls 0.24.2", "indicatif", "integer-encoding", "itertools 0.10.5", @@ -9889,6 +10275,37 @@ dependencies = [ "workspace-hack", ] +[[package]] +name = "sui-test-transaction-builder" +version = "0.1.0-canonical-sui" +dependencies = [ + "bcs 0.1.6", + "move-core-types 0.0.4-canonical-sui", + "shared-crypto", + "sui-genesis-builder", + "sui-move-build", + "sui-sdk", + "sui-types", + "workspace-hack", +] + +[[package]] +name = "sui-transaction-builder" +version = "0.0.0-canonical-sui" +dependencies = [ + "anyhow", + "async-trait", + "bcs 0.1.6", + "futures 0.3.28", + "move-binary-format 0.0.3-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "sui-json", + "sui-json-rpc-types", + "sui-protocol-config", + "sui-types", + "workspace-hack", +] + [[package]] name = "sui-transaction-checks" version = "0.1.0-canonical-sui" @@ -10415,13 +10832,24 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls 0.20.9", + "tokio", + "webpki", +] + [[package]] name = "tokio-rustls" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.8", "tokio", ] @@ -10474,6 +10902,7 @@ checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -10570,10 +10999,10 @@ dependencies = [ "percent-encoding 2.3.0", "pin-project", "prost 0.12.1", - "rustls", + "rustls 0.21.8", "rustls-pemfile", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tokio-stream", "tower", "tower-layer", @@ -10947,6 +11376,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "unescape" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e" + [[package]] name = "unic-char-property" version = "0.9.0" @@ -11175,6 +11610,16 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "versions" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee97e1d97bd593fb513912a07691b742361b3dd64ad56f2c694ea2dbfe0665d3" +dependencies = [ + "itertools 0.10.5", + "nom 7.1.3", +] + [[package]] name = "wait-timeout" version = "0.2.0" @@ -11304,6 +11749,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring 0.17.5", + "untrusted 0.9.0", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + [[package]] name = "webpki-roots" version = "0.25.2" diff --git a/canonical/Cargo.toml b/canonical/Cargo.toml index f43d12285..a6b93c9ac 100644 --- a/canonical/Cargo.toml +++ b/canonical/Cargo.toml @@ -32,6 +32,20 @@ anyhow = "1.0.44" futures = "0.3.17" rand = "0.8.4" bcs = "0.1.0" +tempfile = "3.2.0" +tracing = "0.1.37" +tracing-appender = "0.2.2" +tracing-subscriber = { version = "0.3.15", default-features = false, features = [ + "std", + "smallvec", + "fmt", + "ansi", + "time", + "json", + "registry", + "env-filter", +] } +ctor = "0.2.5" # aptos aptos-framework = { path = "../vendors/aptos-core/aptos-move/framework" } @@ -45,10 +59,9 @@ aptos-storage-interface = { path = "../vendors/aptos-core/storage/storage-interf sui-adapter-latest = { path = "../vendors/sui/sui-execution/latest/sui-adapter" } sui-types = { path = "../vendors/sui/crates/sui-types" } sui-core = { path = "../vendors/sui/crates/sui-core" } +sui-swarm-config = { path = "../vendors/sui/crates/sui-swarm-config" } +sui-test-transaction-builder = { path = "../vendors/sui/crates/sui-test-transaction-builder" } # movement movement-sdk = { path = "../movement-sdk/movement-sdk" } -# patches -[replace] -"move-abigen:0.1.0" = { path = "../vendors/sui/external-crates/move/move-prover/move-abigen" } \ No newline at end of file diff --git a/canonical/README.md b/canonical/README.md new file mode 100644 index 000000000..365a6eed7 --- /dev/null +++ b/canonical/README.md @@ -0,0 +1,66 @@ +# Canonical Execution Layer and Supporting Layers + +© 2023, Movement Labs. All rights reserved. + +## Summary +Provide a Canonical Execution layer that supports behavior analogous to popular Move networks and idioms including Sui and the, currently, internal Movement VM. In addition to the execution layer, provide supporting logic which enables inter-execution-layer compatibility and analogous behavior to the execution layer upstream. + +## Motivation +To create an execution layer that is (a) capable of supporting popular Move idioms, (b) interoperating between different idioms without the need for messaging or bridging, and (c) portable to other consensus and settlement layers via the `movement-sdk`. + + +## Requirements +At the time of the writing, Canonical Execution Layer will support the following "VMs": +- Sui +- Movement + +The support for Sui SHALL include an analogous RPC to the Sui upstream. + +The support for Movement SHALL include an analogous RPC to the Movement upstream. + +The Sui and Movement execution paths SHALL be interoperable. + +The initial deployment of Canonical Execution Layer SHALL be as an Avalanche subnet. That is, consensus and settlement SHALL be handled by the Avalanche consensus engine. + +## User Journeys + +### Canonical Subnet +- Developer can Submit a Sui Transaction to the network for execution and expect that accepted Sui transactions will be executed in a manner analogous to the Sui upstream. +- Developer can Submit a Movement Transaction to the network for execution and expect that accepted Movement transactions are executed in a manner analogous to the Movement upstream. +- Developer can make requests to Sui routes within the Canonical Subnet RPC and received responses analogous to the Sui upstream. +- Developer can make requests to Movement routes within the Canonical Subnet RPC and received responses analogous to the Movement upstream. +- Developer can operate on states altered by Movement transactions via Sui transactions and vice versa. + +## Appendices + +### [A1] Stage 1: Standalone Sui Subnet +#### Summary +To better understand the intricacies of Sui while delivering a new network, this project SHALL begin with the construction of a Sui Avalanche subnet. This subnet shall feature the Sui Block Execution Layer which will ideally later be used to compose the Canonical Execution Layer. + +The requirements for creating analogous structures to the Sui upstream stipulated for the canonical remain firmly in place. + +#### Motivation +- Better understand Sui execution. +- Provide Sui-compatible network. +- Be able to include a Sui execution layer within the `movement-sdk`. +- With the successful extraction of Sui into a Sui Block Executor and other analogous structure, we may be able offer Mysten Labs an implementation of Sui as L2--a goal reported to us via the BD team. + +### [A2] Interoperation via Common Storage +One way to achieve interoperation between different execution layers is to have them share a common move resolver or storage layer. This would allow for the execution layers to operate on the same data. + +**N1**: Synchronization between the execution layers would be required to ensure that the data is consistent. +**N2**: This proposal is conceptually similar to the "transaction multiplexing" first suggested in [MIP-3](https://github.com/movemntdev/MIPs/blob/main/MIPs/mip-3.md) + +![Common Storage](./rsc/canonical_shared_resolver.png) + + +### [A3] Sui Block Execution Layer +In order to achieve Sui-like execution in a block-based consensus network, it has been recommended a point of contact at Mysten Labs that we utilize the checkpoint execution path in the current Sui source. This appears as below: +![Sui Block Executor](./rsc/sui_block_executor.png) + +### [A4] Implementation of Canonical Subnet +#### Summary +The first deployment of the Canonical Execution Layer will be as an Avalanche subnet. The subnet Execution Layer will be composed of the Sui Block Executor and the Movement Block Executor. The subnet will be configured to use the Avalanche consensus engine for consensus and settlement. + +### [A5] Stage 3: Integration of Sui Block Executor and Canonical Execution Layer with Movement-SDK +WIP \ No newline at end of file diff --git a/canonical/canonical-block-executor/Cargo.toml b/canonical/canonical-block-executor/Cargo.toml index 15a1300db..d954e958b 100644 --- a/canonical/canonical-block-executor/Cargo.toml +++ b/canonical/canonical-block-executor/Cargo.toml @@ -24,12 +24,17 @@ jsonrpc-core = "18.0.0" jsonrpc-core-client = { version = "18.0.0" } jsonrpc-derive = "18.0.0" async-channel = "1.9.0" +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +ctor = { workspace = true } +# general anyhow = { workspace = true } futures = { workspace = true } rand = { workspace = true } bcs = { workspace = true } movement-sdk = { workspace = true } +tempfile = { workspace = true } # sui # todo: conflicting rocksdb means we can't use workspace @@ -39,3 +44,5 @@ movement-sdk = { workspace = true } sui-adapter-latest = { workspace = true } sui-types = { workspace = true } sui-core = { workspace = true } +sui-swarm-config = { workspace = true } +sui-test-transaction-builder = { workspace = true} diff --git a/canonical/canonical-block-executor/src/canonical_block_executor/mod.rs b/canonical/canonical-block-executor/src/canonical_block_executor/mod.rs index a977943da..4954a489e 100644 --- a/canonical/canonical-block-executor/src/canonical_block_executor/mod.rs +++ b/canonical/canonical-block-executor/src/canonical_block_executor/mod.rs @@ -1,6 +1,6 @@ -use movement_sdk::{ExecutionLayer}; +/*use movement_sdk::{ExecutionLayer}; use canonical_move_resolver::CanonicalMoveResolver; -use canonical_types::{Transaction, Block}; +use canonical_types::{Transaction, Block};*/ pub mod sui_block_executor; @@ -8,7 +8,7 @@ pub mod sui_block_executor; // todo: type-state pattern structs that handle the bootstrapping // todo: or else we will move that to a higher order // todo: good thing to consider for the movement_sdk -pub struct CanonicalBlockExecutionLayer<'state> { +/*pub struct CanonicalBlockExecutionLayer<'state> { move_resolver : CanonicalMoveResolver<'state> } @@ -68,4 +68,4 @@ impl <'state> CanonicalBlockExecutionLayer<'state> { } -} \ No newline at end of file +}*/ \ No newline at end of file diff --git a/canonical/canonical-block-executor/src/canonical_block_executor/sui_block_executor/mod.rs b/canonical/canonical-block-executor/src/canonical_block_executor/sui_block_executor/mod.rs index f72504abc..8d0044117 100644 --- a/canonical/canonical-block-executor/src/canonical_block_executor/sui_block_executor/mod.rs +++ b/canonical/canonical-block-executor/src/canonical_block_executor/sui_block_executor/mod.rs @@ -1,53 +1,235 @@ -use std::sync::{ - Arc +use core::fmt; +use std::sync::Arc; +use tracing::{debug, error, info, warn}; +use tokio::sync::RwLock; +use sui_core::{ + self, + transaction_manager::TransactionManager, + checkpoints::{ + checkpoint_executor::{CheckpointExecutor, self}, + CheckpointStore + }, + authority::{ + authority_per_epoch_store::AuthorityPerEpochStore, + AuthorityStore, + AuthorityState, + test_authority_builder::TestAuthorityBuilder + }, + state_accumulator::StateAccumulator, }; - -use tokio::sync::{RwLock}; - -use sui_core::checkpoints::checkpoint_executor::{ - CheckpointExecutor +use sui_types::{ + messages_checkpoint::VerifiedCheckpoint, + transaction::{Transaction, VerifiedTransaction}, + executable_transaction::VerifiedExecutableTransaction }; +use tokio::sync::broadcast::{self, Sender, Receiver}; +use sui_swarm_config::test_utils::CommitteeFixture; +use tempfile::tempdir; +use tracing_subscriber::field::debug; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct SuiBlockExecutor { - checkpoint_executor: Arc> + pub authority_state : Arc, + pub checkpoint_executor : Arc>, + pub state_accumulator : Arc, + pub verified_checkpoint_sender : Sender, + pub committee_fixture : Arc, +} + +impl fmt::Debug for SuiBlockExecutor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SuiBlockExecutor") + .finish() + } } -/*async fn init_executor_test( - buffer_size: usize, - store: Arc, -) -> ( - Arc, - CheckpointExecutor, - Arc, - Sender, - CommitteeFixture, -) { - let network_config = +impl SuiBlockExecutor { + + pub fn new( + authority_state : Arc, + checkpoint_executor : Arc>, + state_accumulator : Arc, + verified_checkpoint_sender : Sender, + committee_fixture : Arc, + ) -> Self { + Self { + authority_state, + checkpoint_executor, + state_accumulator, + verified_checkpoint_sender, + committee_fixture, + } + } + + pub async fn init( + buffer_size : usize, + path : Option, + )-> Result { + + let checkpoint_store_path = match path { + Some(path_buf) => path_buf.as_path().to_owned(), + None => { + let dir = tempdir()?; + dir.path().to_owned() + }, + }; + + let store = CheckpointStore::new(&checkpoint_store_path); + + let network_config = sui_swarm_config::network_config_builder::ConfigBuilder::new_with_temp_dir().build(); - let state = TestAuthorityBuilder::new() - .with_network_config(&network_config) - .build() - .await; - - let (checkpoint_sender, _): (Sender, Receiver) = - broadcast::channel(buffer_size); - - let accumulator = StateAccumulator::new(state.database.clone()); - let accumulator = Arc::new(accumulator); - - let executor = CheckpointExecutor::new_for_tests( - checkpoint_sender.subscribe(), - store.clone(), - state.database.clone(), - state.transaction_manager().clone(), - accumulator.clone(), - ); - ( - state, - executor, - accumulator, - checkpoint_sender, - CommitteeFixture::from_network_config(&network_config), - ) -}*/ + let state = TestAuthorityBuilder::new() + .with_network_config(&network_config) + .build() + .await; + + let (checkpoint_sender, _): (Sender, Receiver) = + broadcast::channel(buffer_size); + + let accumulator = StateAccumulator::new(state.database.clone()); + let accumulator = Arc::new(accumulator); + + let executor = CheckpointExecutor::new_for_tests( + checkpoint_sender.subscribe(), + store.clone(), + state.database.clone(), + state.transaction_manager().clone(), + accumulator.clone(), + ); + + Ok(Self::new( + state, + Arc::new(RwLock::new(executor)), + accumulator, + checkpoint_sender, + Arc::new(CommitteeFixture::from_network_config(&network_config)), + )) + + } + + pub async fn execute_block(&self, block : Vec)-> Result<(), anyhow::Error> { + + let epoch_store = { + let state = self.authority_state.clone(); + + // Enqueue transactions in the transaction manager + let transaction_manager: Arc = state.transaction_manager().clone(); + let epoch_store = state.epoch_store_for_testing().to_owned(); + debug!("Enqueueing transactions..."); + transaction_manager.enqueue(block, &epoch_store)?; + debug!("Enqueued transactions"); + epoch_store + }; + + // Run the epoch + // todo: confirm running the epoch waits for all transactions to be complete + { + let mut checkpoint_executor = self.checkpoint_executor.write().await; + checkpoint_executor.run_epoch(epoch_store).await; + + } + + Ok(()) + + } + +} + +#[cfg(test)] +mod test { + + use super::*; + use tracing::{debug, error, info, warn}; + use tracing_subscriber::{FmtSubscriber, EnvFilter}; + + use sui_test_transaction_builder::TestTransactionBuilder; + + use sui_types::{ + base_types::ObjectID, + crypto::deterministic_random_account_key, + digests::TransactionEffectsDigest, + object::Object, + storage::InputKey, + transaction::{CallArg, ObjectArg}, + SUI_FRAMEWORK_PACKAGE_ID, + }; + + #[ctor::ctor] + fn before_all() { + // Create a filter based on the RUST_LOG environment variable + let filter = EnvFilter::from_default_env(); + + // Create a FmtSubscriber with the filter + let subscriber = FmtSubscriber::builder() + .with_env_filter(filter) + .finish(); + + // Set the subscriber as the global default + tracing::subscriber::set_global_default(subscriber).expect("Failed to set the global tracing subscriber"); + } + + fn make_transaction(gas_object: Object, input: Vec) -> VerifiedExecutableTransaction { + // Use fake module, function, package and gas prices since they are irrelevant for testing + // transaction manager. + let rgp = 100; + let (sender, keypair) = deterministic_random_account_key(); + let transaction = + TestTransactionBuilder::new(sender, gas_object.compute_object_reference(), rgp) + .move_call(SUI_FRAMEWORK_PACKAGE_ID, "counter", "assert_value", input) + .build_and_sign(&keypair); + VerifiedExecutableTransaction::new_system(VerifiedTransaction::new_unchecked(transaction), 0) + } + + #[tokio::test] + async fn test_empty_block() { + + let executor = super::SuiBlockExecutor::init(10, None).await.unwrap(); + + let block = vec![]; + + executor.execute_block(block).await.unwrap(); + + } + + #[tokio::test] + async fn test_genesis_transaction() { + + let (owner, _keypair) = deterministic_random_account_key(); + + let executor = super::SuiBlockExecutor::init(10, None).await.unwrap(); + + let transaction = VerifiedExecutableTransaction::new_system( + VerifiedTransaction::new_genesis_transaction(vec![]), + 0 + ); + let block = vec![transaction]; + + executor.execute_block(block).await.unwrap(); + } + + + #[tokio::test] + async fn test_single_transaction() { + + /*let (owner, _keypair) = deterministic_random_account_key(); + let gas_objects: Vec = (0..10) + .map(|_| { + let gas_object_id = ObjectID::random(); + Object::with_id_owner_for_testing(gas_object_id, owner) + }) + .collect(); + + let executor = super::SuiBlockExecutor::init(10, None).await.unwrap(); + + let genesis_transaction = VerifiedExecutableTransaction::new_system( + VerifiedTransaction::new_genesis_transaction(gas_objects.clone().), + 0 + ); + let transaction = make_transaction(gas_objects[0].clone(), vec![]); + let block = vec![transaction]; + + executor.execute_block(block).await.unwrap();*/ + + } + +} \ No newline at end of file diff --git a/canonical/canonical-block-executor/src/lib.rs b/canonical/canonical-block-executor/src/lib.rs index e69de29bb..e0f3abad3 100644 --- a/canonical/canonical-block-executor/src/lib.rs +++ b/canonical/canonical-block-executor/src/lib.rs @@ -0,0 +1 @@ +pub mod canonical_block_executor; \ No newline at end of file diff --git a/canonical/rsc/canonical_shared_resolver.png b/canonical/rsc/canonical_shared_resolver.png new file mode 100644 index 0000000000000000000000000000000000000000..3e52c696cc7998b1c25848f01a8d5edc68e4b99a GIT binary patch literal 108418 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 H&e8uLUNPDa literal 0 HcmV?d00001 diff --git a/canonical/rsc/sui_block_executor.png b/canonical/rsc/sui_block_executor.png new file mode 100644 index 0000000000000000000000000000000000000000..e3eec52213ae21c9a166ed7264e1fc5e12df92a7 GIT binary patch literal 80085 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 Q{mVgd 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 literal 0 HcmV?d00001 diff --git a/movement-sdk/Cargo.lock b/movement-sdk/Cargo.lock index 82498141b..f7361660a 100644 --- a/movement-sdk/Cargo.lock +++ b/movement-sdk/Cargo.lock @@ -190,7 +190,7 @@ dependencies = [ "thiserror", "tokio", "tokio-stream", - "tonic 0.9.2", + "tonic", "tonic-health", "tonic-reflection", "tower-service", @@ -415,9 +415,11 @@ dependencies = [ "anyhow", "async-trait", "avalanche-types", + "ctor", "movement-sdk", + "serde", + "serde_json", "tokio", - "tonic 0.8.3", ] [[package]] @@ -499,6 +501,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctor" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e366bff8cd32dd8754b0991fb66b279dc48f598c3a18914852a6673deef583" +dependencies = [ + "quote", + "syn 2.0.38", +] + [[package]] name = "darling" version = "0.20.3" @@ -2522,39 +2534,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "tonic" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" -dependencies = [ - "async-stream", - "async-trait", - "axum", - "base64 0.13.1", - "bytes", - "flate2", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-timeout", - "percent-encoding", - "pin-project", - "prost", - "prost-derive", - "tokio", - "tokio-stream", - "tokio-util", - "tower", - "tower-layer", - "tower-service", - "tracing", - "tracing-futures", -] - [[package]] name = "tonic" version = "0.9.2" @@ -2594,7 +2573,7 @@ dependencies = [ "prost", "tokio", "tokio-stream", - "tonic 0.9.2", + "tonic", ] [[package]] @@ -2607,7 +2586,7 @@ dependencies = [ "prost-types", "tokio", "tokio-stream", - "tonic 0.9.2", + "tonic", ] [[package]] @@ -2673,16 +2652,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - [[package]] name = "try-lock" version = "0.2.4" diff --git a/movement-sdk/Cargo.toml b/movement-sdk/Cargo.toml index 685f92bbd..6dea2cffc 100644 --- a/movement-sdk/Cargo.toml +++ b/movement-sdk/Cargo.toml @@ -21,4 +21,6 @@ anyhow = { version = "1" } # For flexible error handling avalanche-types = { version = "0.1.3", features = ["subnet", "codec_base64"] } movement-sdk = { path = "movement-sdk" } tokio = { version = "1.21.0", features = ["full"] } -serde = { version = "1.0", features = ["derive"] } \ No newline at end of file +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0", features = ["raw_value"] } +ctor = "0.2.5" \ No newline at end of file diff --git a/movement-sdk/movement-sdk-avalanche/Cargo.toml b/movement-sdk/movement-sdk-avalanche/Cargo.toml index fcc4c733a..d36add64e 100644 --- a/movement-sdk/movement-sdk-avalanche/Cargo.toml +++ b/movement-sdk/movement-sdk-avalanche/Cargo.toml @@ -9,4 +9,6 @@ anyhow = { workspace = true } avalanche-types = { workspace = true } movement-sdk = { workspace = true } tokio = { workspace = true } -tonic = { version = "0.8.3", features = ["gzip"] } \ No newline at end of file +serde = { workspace = true } +serde_json = { workspace = true } +ctor = { workspace = true } \ No newline at end of file diff --git a/movement-sdk/movement-sdk-avalanche/src/data_availability/block.rs b/movement-sdk/movement-sdk-avalanche/src/data_availability/block.rs new file mode 100644 index 000000000..5b6547b34 --- /dev/null +++ b/movement-sdk/movement-sdk-avalanche/src/data_availability/block.rs @@ -0,0 +1,167 @@ +use avalanche_types::{ + subnet, + choices, + ids::{self, Id}, + subnet::rpc::consensus::snowman::{ + Decidable, + Block as Blockable + } +}; +use movement_sdk::{Layer, DataAvailabilityLayer}; +use std::sync::Arc; +use tokio::sync::RwLock; +use std::io; + +#[derive(Debug, Clone)] +pub struct AvalancheBlock< + Block : Send + Sync, + BlockId : Send + Sync, + DA : DataAvailabilityLayer +>{ + pub inner_block: Arc, + pub data_availability_layer: Arc>, +} + +impl < + Block : Send + Sync, + BlockId : Send + Sync, + DA : DataAvailabilityLayer +> AvalancheBlock { + + pub fn new( + inner_block: Block, + data_availability_layer: Arc>, + ) -> Self { + Self { + inner_block : Arc::new(RwLock::new(inner_block)), + data_availability_layer, + } + } + +} + +impl < + Block : Send + Sync, + BlockId : Send + Sync, + DA : DataAvailabilityLayer +> Into> for (Block, Arc>) { + fn into(self) -> AvalancheBlock { + AvalancheBlock::new(self.0, self.1) + } +} + +#[async_trait::async_trait] +impl < + Block : Decidable + Clone + Send + Sync, + BlockId : Send + Sync, + DA : DataAvailabilityLayer + Sync + Send +> Decidable for AvalancheBlock { + + async fn id(&self) -> ids::Id { + self.inner_block.id().await + } + + async fn status(&self) -> choices::status::Status { + self.inner_block.status().await + } + + async fn accept(&mut self) -> io::Result<()> { + let data_availability_layer = self.data_availability_layer.read().await; + let block_copy = self.inner_block.read().await.clone(); + let result = data_availability_layer.accept_block(block_copy).await; + result.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + Ok(()) + } + + async fn reject(&mut self) -> io::Result<()> { + let data_availability_layer = self.data_availability_layer.read().await; + let block_copy = self.inner_block.read().await.clone(); + let result = data_availability_layer.reject_block(block_copy).await; + result.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + Ok(()) + } + +} + +#[cfg(test)] +mod test { + + use super::*; + + #[derive(Debug, Clone)] + pub struct MyBlock(String); + + #[async_trait::async_trait] + impl Decidable for MyBlock { + async fn id(&self) -> ids::Id { + ids::Id::sha256(self.0.as_bytes()) + } + + async fn status(&self) -> choices::status::Status { + choices::status::Status::Unknown + } + + async fn accept(&mut self) -> io::Result<()> { + Ok(()) + } + + async fn reject(&mut self) -> io::Result<()> { + Ok(()) + } + } + + #[derive(Debug, Clone)] + pub struct MyDataAvailabilityLayer; + + impl Layer for MyDataAvailabilityLayer {} + + #[async_trait::async_trait] + impl DataAvailabilityLayer for MyDataAvailabilityLayer { + type Block = MyBlock; + type BlockId = ids::Id; + + async fn get_next_block( + &self + ) -> Result, anyhow::Error> { + Ok(None) + } + + async fn accept_block( + &self, + block: Self::Block + ) -> Result<(), anyhow::Error> { + Ok(()) + } + + async fn reject_block( + &self, + block: Self::Block + ) -> Result<(), anyhow::Error> { + Ok(()) + } + + async fn get_block( + &self, + block_id: Self::BlockId + ) -> Result, anyhow::Error> { + Ok(None) + } + } + + #[tokio::test] + async fn test_avalanche_block() { + let data_availability_layer = Arc::new(RwLock::new(MyDataAvailabilityLayer)); + let block = MyBlock("hello".to_string()); + let avalanche_block = AvalancheBlock::new(block, data_availability_layer); + let id = avalanche_block.id().await; + assert_eq!(id, ids::Id::sha256("hello".as_bytes())); + let status = avalanche_block.status().await; + assert_eq!(status, choices::status::Status::Unknown); + let result = avalanche_block.accept().await; + assert!(result.is_ok()); + let result = avalanche_block.reject().await; + assert!(result.is_ok()); + } + + +} diff --git a/movement-sdk/movement-sdk-avalanche/src/data_availability/data_availability_layer.rs b/movement-sdk/movement-sdk-avalanche/src/data_availability/data_availability_layer.rs new file mode 100644 index 000000000..a3b4d6ec3 --- /dev/null +++ b/movement-sdk/movement-sdk-avalanche/src/data_availability/data_availability_layer.rs @@ -0,0 +1,97 @@ +use avalanche_types::{ + subnet, + choices, + ids::{self, Id}, + subnet::rpc::consensus::snowman::{ + Decidable, + Block as Blockable + } +}; +use movement_sdk::DataAvailabilityLayer; +use std::sync::Arc; +use tokio::sync::RwLock; +use std::io; +use std::collections::HashMap; +use serde::{de::DeserializeOwned, Serialize}; +use serde_json; + +pub struct AvalancheDataAvailabilityLayer { + pub db: Arc>>, + pub verified_blocks: Arc>>, +} + +impl AvalancheDataAvailabilityLayer { + + pub fn get_block_with_status_key( + block : &Block + ) -> Result { + let bytes = serde_json::to_vec(&block)?; + let id = ids::Id::from_slice(&bytes.as_slice()); + Ok(id) + } + + pub async fn write_block( + &self, + block: &Block + ) -> Result<(), anyhow::Error> { + let mut db = self.db.write().await; + let key = Self::get_block_with_status_key(block)?.to_vec().as_slice(); + let value = serde_json::to_vec(block)?.as_slice(); + db.put(key, value).await?; + Ok(()) + } + +} + +impl AvalancheDataAvailabilityLayer { + + pub fn new( + db: Arc>>, + verified_blocks: Arc>>, + ) -> Self { + Self { + db, + verified_blocks, + } + } + +} + +#[async_trait::async_trait] +impl DataAvailabilityLayer for AvalancheDataAvailabilityLayer { + + type Block = Block; + type BlockId = BlockId; + + /// Gets the next block from the previous layer. + async fn get_next_block( + &self + ) -> Result, anyhow::Error> { + todo!() + } + + /// Accepts a block, effectively sending it to the next layer or place retrievable from the next layer, i.e., the execution layer. + async fn accept_block( + &self, + block: Self::Block + ) -> Result<(), anyhow::Error> { + todo!() + } + + /// Rejects a block (sometimes this won't be used). + async fn reject_block( + &self, + block: Self::Block + ) -> Result<(), anyhow::Error> { + todo!() + } + + /// Gets a block that was either accepted or rejected by the data availability layer. + async fn get_block( + &self, + block_id: Self::BlockId + ) -> Result, anyhow::Error> { + todo!() + } + +} \ No newline at end of file diff --git a/movement-sdk/movement-sdk-avalanche/src/data_availability/mod.rs b/movement-sdk/movement-sdk-avalanche/src/data_availability/mod.rs index c98ee9dea..413eab3f5 100644 --- a/movement-sdk/movement-sdk-avalanche/src/data_availability/mod.rs +++ b/movement-sdk/movement-sdk-avalanche/src/data_availability/mod.rs @@ -1,82 +1,2 @@ -use avalanche_types::{ - choices, - ids::{self, Id}, - subnet::rpc::consensus::snowman::{ - Decidable, - Block - } -}; -use movement_sdk::DataAvailabilityLayer; -use std::sync::Arc; -use tokio::sync::RwLock; -use std::io; - -#[derive(Debug, Clone)] -pub struct AvalancheBlock< - Block, - BlockId, - DA : DataAvailabilityLayer ->{ - pub inner_block: Block, - pub data_availability_layer: Arc>, -} - -impl < - Block, - BlockId, - DA : DataAvailabilityLayer -> AvalancheBlock { - - pub fn new( - inner_block: Block, - data_availability_layer: Arc>, - ) -> Self { - Self { - inner_block, - data_availability_layer, - } - } - -} - -impl < - Block, - BlockId, - DA : DataAvailabilityLayer -> Into> for (Block, Arc>) { - fn into(self) -> AvalancheBlock { - AvalancheBlock::new(self.0, self.1) - } -} - -#[async_trait::async_trait] -impl < - Block : Sync + Clone, - BlockId, - DA : DataAvailabilityLayer + Sync + Send -> Decidable for AvalancheBlock { - - async fn id(&self) -> ids::Id { - unimplemented!("todo: implement id"); - // ids::Id::empty() - } - - /// Implements "snowman.Block.choices.Decidable" - async fn status(&self) -> choices::status::Status { - unimplemented!("todo: implement status"); - // choices::status::Status::Unknown(String::from("unimplemented")) - } - - async fn accept(&mut self) -> io::Result<()> { - let data_availability_layer = self.data_availability_layer.read().await; - data_availability_layer.accept_block(self.inner_block.clone()).await?; - Ok(()) - } - - async fn reject(&mut self) -> io::Result<()> { - let data_availability_layer = self.data_availability_layer.read().await; - data_availability_layer.reject_block(self.inner_block.clone()).await?; - Ok(()) - } - -} \ No newline at end of file +pub mod data_availability_layer; +pub mod block; \ No newline at end of file diff --git a/movement-sdk/movement-sdk/src/lib.rs b/movement-sdk/movement-sdk/src/lib.rs index 7d87be97b..f537306ab 100644 --- a/movement-sdk/movement-sdk/src/lib.rs +++ b/movement-sdk/movement-sdk/src/lib.rs @@ -1,11 +1,25 @@ use async_trait::async_trait; use core::fmt::Debug; -use serde::{Deserialize, Serialize}; +/// Marker trait for all Layers. pub trait Layer : Debug + Clone { } +#[async_trait] +pub trait VerifierLayer : Layer { + + type Block; + type BlockId; + + async fn is_valid_block( + &self, + block: Self::Block + ) -> Result<(bool, Self::Block), anyhow::Error>; + +} + + // Top-level definition of traits. // Complex extensions and integrations should be defined in the submodules. #[async_trait] From 141126c230ae84de4ff6dbdacd18809080a4633f Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 28 Nov 2023 14:25:59 -0800 Subject: [PATCH 02/58] chore: structuring integrated movement CLI. --- movement-sdk/Cargo.toml | 5 ++++- movement-sdk/clis/movement/Cargo.toml | 10 ++++++++++ movement-sdk/clis/movement/README.md | 1 + movement-sdk/clis/movement/src/aptos/README.md | 2 ++ movement-sdk/clis/movement/src/aptos/mod.rs | 0 .../clis/movement/src/canonical/README.md | 2 ++ movement-sdk/clis/movement/src/canonical/mod.rs | 0 movement-sdk/clis/movement/src/common/README.md | 3 +++ movement-sdk/clis/movement/src/common/mod.rs | 0 movement-sdk/clis/movement/src/ctl/README.md | 2 ++ movement-sdk/clis/movement/src/ctl/mod.rs | 0 movement-sdk/clis/movement/src/lib.rs | 7 +++++++ movement-sdk/clis/movement/src/main.rs | 15 +++++++++++++++ movement-sdk/clis/movement/src/manage/README.md | 2 ++ movement-sdk/clis/movement/src/manage/mod.rs | 0 movement-sdk/clis/movement/src/sui/README.md | 2 ++ movement-sdk/clis/movement/src/sui/mod.rs | 0 17 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 movement-sdk/clis/movement/Cargo.toml create mode 100644 movement-sdk/clis/movement/README.md create mode 100644 movement-sdk/clis/movement/src/aptos/README.md create mode 100644 movement-sdk/clis/movement/src/aptos/mod.rs create mode 100644 movement-sdk/clis/movement/src/canonical/README.md create mode 100644 movement-sdk/clis/movement/src/canonical/mod.rs create mode 100644 movement-sdk/clis/movement/src/common/README.md create mode 100644 movement-sdk/clis/movement/src/common/mod.rs create mode 100644 movement-sdk/clis/movement/src/ctl/README.md create mode 100644 movement-sdk/clis/movement/src/ctl/mod.rs create mode 100644 movement-sdk/clis/movement/src/lib.rs create mode 100644 movement-sdk/clis/movement/src/main.rs create mode 100644 movement-sdk/clis/movement/src/manage/README.md create mode 100644 movement-sdk/clis/movement/src/manage/mod.rs create mode 100644 movement-sdk/clis/movement/src/sui/README.md create mode 100644 movement-sdk/clis/movement/src/sui/mod.rs diff --git a/movement-sdk/Cargo.toml b/movement-sdk/Cargo.toml index 6dea2cffc..a6daabab6 100644 --- a/movement-sdk/Cargo.toml +++ b/movement-sdk/Cargo.toml @@ -23,4 +23,7 @@ movement-sdk = { path = "movement-sdk" } tokio = { version = "1.21.0", features = ["full"] } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["raw_value"] } -ctor = "0.2.5" \ No newline at end of file +ctor = "0.2.5" + +# todo: bump clap to most recent version +clap = { version = "3.2.23", features = ["derive", "env", "suggestions"] } \ No newline at end of file diff --git a/movement-sdk/clis/movement/Cargo.toml b/movement-sdk/clis/movement/Cargo.toml new file mode 100644 index 000000000..c86b1cbaf --- /dev/null +++ b/movement-sdk/clis/movement/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "movement" +version = "0.1.0" +edition = "2021" + +[dependencies] +async-trait = { workspace = true } +anyhow = { workspace = true } +serde = { workspace = true } +clap = { workspace = true } \ No newline at end of file diff --git a/movement-sdk/clis/movement/README.md b/movement-sdk/clis/movement/README.md new file mode 100644 index 000000000..8e0f05e30 --- /dev/null +++ b/movement-sdk/clis/movement/README.md @@ -0,0 +1 @@ +# `movement-cli` \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/aptos/README.md b/movement-sdk/clis/movement/src/aptos/README.md new file mode 100644 index 000000000..8d90ce312 --- /dev/null +++ b/movement-sdk/clis/movement/src/aptos/README.md @@ -0,0 +1,2 @@ +# `aptos` +The `aptos` subcommand roughly subsumes the Aptos CLI. \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/aptos/mod.rs b/movement-sdk/clis/movement/src/aptos/mod.rs new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/clis/movement/src/canonical/README.md b/movement-sdk/clis/movement/src/canonical/README.md new file mode 100644 index 000000000..778a4ba83 --- /dev/null +++ b/movement-sdk/clis/movement/src/canonical/README.md @@ -0,0 +1,2 @@ +# `canonical` +The `canonical` subcommand contains commands for working against a canonical execution layer. \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/canonical/mod.rs b/movement-sdk/clis/movement/src/canonical/mod.rs new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/clis/movement/src/common/README.md b/movement-sdk/clis/movement/src/common/README.md new file mode 100644 index 000000000..5a6391378 --- /dev/null +++ b/movement-sdk/clis/movement/src/common/README.md @@ -0,0 +1,3 @@ +# `common` +This directory contains common structs and functions used by the subcommands in the Movement CLI. + diff --git a/movement-sdk/clis/movement/src/common/mod.rs b/movement-sdk/clis/movement/src/common/mod.rs new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/clis/movement/src/ctl/README.md b/movement-sdk/clis/movement/src/ctl/README.md new file mode 100644 index 000000000..620333895 --- /dev/null +++ b/movement-sdk/clis/movement/src/ctl/README.md @@ -0,0 +1,2 @@ +# `ctl` +The `ctl` subcommand is used to control the running of Movement services. \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/mod.rs b/movement-sdk/clis/movement/src/ctl/mod.rs new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/clis/movement/src/lib.rs b/movement-sdk/clis/movement/src/lib.rs new file mode 100644 index 000000000..94d96d2e4 --- /dev/null +++ b/movement-sdk/clis/movement/src/lib.rs @@ -0,0 +1,7 @@ +pub mod common; + +pub mod manage; +pub mod ctl; +pub mod aptos; +pub mod sui; +pub mod canonical; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/main.rs b/movement-sdk/clis/movement/src/main.rs new file mode 100644 index 000000000..4c5f216c5 --- /dev/null +++ b/movement-sdk/clis/movement/src/main.rs @@ -0,0 +1,15 @@ +#![forbid(unsafe_code)] + +#[cfg(unix)] +#[global_allocator] +static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; + +use clap::Parser; +use std::process::exit; + +#[tokio::main] +async fn main() { + + // todo: parse clap + +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/manage/README.md b/movement-sdk/clis/movement/src/manage/README.md new file mode 100644 index 000000000..5a6265464 --- /dev/null +++ b/movement-sdk/clis/movement/src/manage/README.md @@ -0,0 +1,2 @@ +# `manage` +The `manage` subcommand is used to manage installations, upgrades, and removals of Movement dependencies. It is also used to manage the Movement CLI itself. \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/manage/mod.rs b/movement-sdk/clis/movement/src/manage/mod.rs new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/clis/movement/src/sui/README.md b/movement-sdk/clis/movement/src/sui/README.md new file mode 100644 index 000000000..87a2203c8 --- /dev/null +++ b/movement-sdk/clis/movement/src/sui/README.md @@ -0,0 +1,2 @@ +# `sui` +The `sui` subcommand roughly subsumes the Sui CLI. \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/sui/mod.rs b/movement-sdk/clis/movement/src/sui/mod.rs new file mode 100644 index 000000000..e69de29bb From 067ca0da346dc7804c95ec084e007dfddb78b020 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 28 Nov 2023 15:17:34 -0800 Subject: [PATCH 03/58] feat: adding branch-specific releases. --- .github/workflows/check.yml | 2 ++ .github/workflows/coverage.yml | 1 + .github/workflows/release.yml | 12 +++++++++--- .github/workflows/test.yml | 1 + 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 80b93dad9..cf566eec0 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -3,6 +3,8 @@ name: Cargo Check on: push: branches: + - dev + - stage - main jobs: diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 1c1de772e..b2a7c84a0 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -17,6 +17,7 @@ jobs: with: submodules: 'recursive' token: ${{ secrets.CI_PAT }} + ref: ${{ github.event.inputs.branch }} - name: Set up Rust uses: actions-rs/toolchain@v1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5d97203b8..4d988c32e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,6 +26,12 @@ jobs: with: submodules: 'recursive' token: ${{ secrets.CI_PAT }} + ref: ${{ github.event.inputs.branch }} + + - name: Set up Branch Name + id: branch_name + run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV + - name: Bump version and push tag id: create_tag uses: anothrNick/github-tag-action@1.64.0 # Don't use @master or @v1 unless you're happy to test the latest version @@ -47,11 +53,11 @@ jobs: id: create_release uses: actions/create-release@v1 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ steps.create_tag.outputs.tag }} - release_name: Release ${{ steps.create_tag.outputs.tag }} - body: Release ${{ steps.create_tag.outputs.tag }} + release_name: ${{ env.BRANCH_NAME == 'main' && 'Release ' || 'Release ' + env.BRANCH_NAME + '-' }}${{ steps.create_tag.outputs.tag }} + body: ${{ env.BRANCH_NAME == 'main' && 'Release ' || 'Release ' + env.BRANCH_NAME + '-' }}${{ steps.create_tag.outputs.tag }} draft: false prerelease: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 08667a0e3..e80c1f528 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,6 +18,7 @@ jobs: with: submodules: 'recursive' token: ${{ secrets.CI_PAT }} + ref: ${{ github.event.inputs.branch }} - name: Setup uses: ./.github/actions/setup-linux-x86_64 From 976edd3d1a4a665659ee3105432803d0e9b766c7 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 28 Nov 2023 16:15:08 -0800 Subject: [PATCH 04/58] cicd: switch to test.debug.sh. --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e80c1f528..f99b1232a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: - name: Build and Test Subnet run: | cd ./m1 - cargo test + ./scripts/tests.debug.sh - name: Update Badge run: | From 2c9ad1992463e15b587dbebd06ec713031a1c8f1 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 28 Nov 2023 16:17:16 -0800 Subject: [PATCH 05/58] chore: merge. --- vendors/sui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendors/sui b/vendors/sui index 5f6756294..845182094 160000 --- a/vendors/sui +++ b/vendors/sui @@ -1 +1 @@ -Subproject commit 5f67562946d4d797a5a7e177f733abcc43d261e9 +Subproject commit 845182094b64f517dcbeab22f6da903f4f5bc7ec From af55b56200d7351b2b44bc247970335dd1ba70c0 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 28 Nov 2023 16:32:17 -0800 Subject: [PATCH 06/58] feat(wip): integrated movementctl. --- movement-sdk/Cargo.lock | 92 ++++++++++++++++++++++- movement-sdk/Cargo.toml | 5 +- movement-sdk/clis/movement/src/ctl/ctl.rs | 7 ++ movement-sdk/clis/movement/src/ctl/mod.rs | 1 + movement-sdk/clis/movement/src/lib.rs | 12 ++- vendors/sui | 2 +- 6 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 movement-sdk/clis/movement/src/ctl/ctl.rs diff --git a/movement-sdk/Cargo.lock b/movement-sdk/Cargo.lock index f7361660a..0efaf36b9 100644 --- a/movement-sdk/Cargo.lock +++ b/movement-sdk/Cargo.lock @@ -125,6 +125,17 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + [[package]] name = "auto_impl" version = "1.1.0" @@ -437,6 +448,45 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_derive", + "clap_lex", + "indexmap 1.9.3", + "once_cell", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "cmp-manager" version = "0.0.1" @@ -981,6 +1031,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hermit-abi" version = "0.3.3" @@ -1320,6 +1379,16 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "movement" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "clap", + "serde", +] + [[package]] name = "movement-sdk" version = "0.1.0" @@ -1415,7 +1484,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.3", "libc", ] @@ -1489,6 +1558,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + [[package]] name = "parity-scale-codec" version = "3.6.5" @@ -2370,6 +2445,21 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "termcolor" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" + [[package]] name = "thiserror" version = "1.0.50" diff --git a/movement-sdk/Cargo.toml b/movement-sdk/Cargo.toml index a6daabab6..df95c6d32 100644 --- a/movement-sdk/Cargo.toml +++ b/movement-sdk/Cargo.toml @@ -2,7 +2,10 @@ resolver = "2" members = [ "movement-sdk", - "movement-sdk-avalanche" + "movement-sdk-avalanche", + + # clis + "clis/movement" ] [workspace.package] diff --git a/movement-sdk/clis/movement/src/ctl/ctl.rs b/movement-sdk/clis/movement/src/ctl/ctl.rs new file mode 100644 index 000000000..a2b48db13 --- /dev/null +++ b/movement-sdk/clis/movement/src/ctl/ctl.rs @@ -0,0 +1,7 @@ +use async_trait::async_trait; +use clap::Parser; + +#[derive(Parser)] +pub enum Ctl { + +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/mod.rs b/movement-sdk/clis/movement/src/ctl/mod.rs index e69de29bb..35e66572c 100644 --- a/movement-sdk/clis/movement/src/ctl/mod.rs +++ b/movement-sdk/clis/movement/src/ctl/mod.rs @@ -0,0 +1 @@ +pub mod ctl; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/lib.rs b/movement-sdk/clis/movement/src/lib.rs index 94d96d2e4..7315e05f9 100644 --- a/movement-sdk/clis/movement/src/lib.rs +++ b/movement-sdk/clis/movement/src/lib.rs @@ -4,4 +4,14 @@ pub mod manage; pub mod ctl; pub mod aptos; pub mod sui; -pub mod canonical; \ No newline at end of file +pub mod canonical; + +use async_trait::async_trait; +use clap::Parser; +use ctl::ctl::Ctl; + +#[derive(Parser)] +#[clap(name = "movement", author, version, propagate_version = true)] +pub enum Movement { + +} \ No newline at end of file diff --git a/vendors/sui b/vendors/sui index 5f6756294..845182094 160000 --- a/vendors/sui +++ b/vendors/sui @@ -1 +1 @@ -Subproject commit 5f67562946d4d797a5a7e177f733abcc43d261e9 +Subproject commit 845182094b64f517dcbeab22f6da903f4f5bc7ec From 5285046962fcfada70fe2fce0d45f87e58bd9e09 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 28 Nov 2023 16:56:03 -0800 Subject: [PATCH 07/58] feat: integrated CLI structuring. --- movement-sdk/clis/movement/src/common/cli/command.rs | 11 +++++++++++ movement-sdk/clis/movement/src/common/cli/mod.rs | 2 ++ movement-sdk/clis/movement/src/common/mod.rs | 1 + movement-sdk/clis/movement/src/ctl/ctl.rs | 4 ++-- movement-sdk/clis/movement/src/ctl/m1/m1.rs | 7 +++++++ movement-sdk/clis/movement/src/ctl/m1/mod.rs | 0 movement-sdk/clis/movement/src/ctl/mod.rs | 6 +++++- movement-sdk/clis/movement/src/ctl/options.rs | 4 ++++ movement-sdk/clis/movement/src/lib.rs | 7 ++++--- 9 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 movement-sdk/clis/movement/src/common/cli/command.rs create mode 100644 movement-sdk/clis/movement/src/common/cli/mod.rs create mode 100644 movement-sdk/clis/movement/src/ctl/m1/m1.rs create mode 100644 movement-sdk/clis/movement/src/ctl/m1/mod.rs create mode 100644 movement-sdk/clis/movement/src/ctl/options.rs diff --git a/movement-sdk/clis/movement/src/common/cli/command.rs b/movement-sdk/clis/movement/src/common/cli/command.rs new file mode 100644 index 000000000..89bed5e04 --- /dev/null +++ b/movement-sdk/clis/movement/src/common/cli/command.rs @@ -0,0 +1,11 @@ +use async_trait::async_trait; +use anyhow; + +#[async_trait] +pub trait Command { + + async fn get_name(&self) -> String; + + async fn execute(&self) -> Result; + +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/cli/mod.rs b/movement-sdk/clis/movement/src/common/cli/mod.rs new file mode 100644 index 000000000..9ee01577f --- /dev/null +++ b/movement-sdk/clis/movement/src/common/cli/mod.rs @@ -0,0 +1,2 @@ +pub mod command; +pub use command::Command; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/mod.rs b/movement-sdk/clis/movement/src/common/mod.rs index e69de29bb..5d863fb78 100644 --- a/movement-sdk/clis/movement/src/common/mod.rs +++ b/movement-sdk/clis/movement/src/common/mod.rs @@ -0,0 +1 @@ +pub mod cli; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/ctl.rs b/movement-sdk/clis/movement/src/ctl/ctl.rs index a2b48db13..7f6f32aaf 100644 --- a/movement-sdk/clis/movement/src/ctl/ctl.rs +++ b/movement-sdk/clis/movement/src/ctl/ctl.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; -use clap::Parser; +use clap::Subcommand; -#[derive(Parser)] +#[derive(Subcommand, Debug)] pub enum Ctl { } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/m1/m1.rs b/movement-sdk/clis/movement/src/ctl/m1/m1.rs new file mode 100644 index 000000000..67048225e --- /dev/null +++ b/movement-sdk/clis/movement/src/ctl/m1/m1.rs @@ -0,0 +1,7 @@ +use async_trait::async_trait; +use clap::Subcommand; + +#[derive(Subcommand, Debug)] +pub enum M1 { + +} diff --git a/movement-sdk/clis/movement/src/ctl/m1/mod.rs b/movement-sdk/clis/movement/src/ctl/m1/mod.rs new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/clis/movement/src/ctl/mod.rs b/movement-sdk/clis/movement/src/ctl/mod.rs index 35e66572c..094a161c2 100644 --- a/movement-sdk/clis/movement/src/ctl/mod.rs +++ b/movement-sdk/clis/movement/src/ctl/mod.rs @@ -1 +1,5 @@ -pub mod ctl; \ No newline at end of file +pub mod ctl; +pub use ctl::Ctl; + +pub mod m1; +pub mod options; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/options.rs b/movement-sdk/clis/movement/src/ctl/options.rs new file mode 100644 index 000000000..5898cc7b7 --- /dev/null +++ b/movement-sdk/clis/movement/src/ctl/options.rs @@ -0,0 +1,4 @@ +#[derive(Debug, Clone)] +pub struct CtlOptions { + foreground: bool +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/lib.rs b/movement-sdk/clis/movement/src/lib.rs index 7315e05f9..123ea19a0 100644 --- a/movement-sdk/clis/movement/src/lib.rs +++ b/movement-sdk/clis/movement/src/lib.rs @@ -8,10 +8,11 @@ pub mod canonical; use async_trait::async_trait; use clap::Parser; -use ctl::ctl::Ctl; +use ctl::Ctl; -#[derive(Parser)] +#[derive(Parser, Debug)] #[clap(name = "movement", author, version, propagate_version = true)] pub enum Movement { - + #[clap(subcommand)] + Ctl(Ctl), } \ No newline at end of file From 775a140d0bf1734692dbeb5d0d0291f65c54eace Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 28 Nov 2023 16:58:10 -0800 Subject: [PATCH 08/58] chore: commenting on build and test subnet. --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f99b1232a..e60f5815d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,6 +23,7 @@ jobs: - name: Setup uses: ./.github/actions/setup-linux-x86_64 + # run the ANR-based subnet tests - name: Build and Test Subnet run: | cd ./m1 From 14fb7c21865c13215e3b60e2177382f42a3ae028 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 29 Nov 2023 15:44:05 -0800 Subject: [PATCH 09/58] fix: workflow should reference follow-on workflows on triggering branch. --- .github/actions/trigger-workflow/action.yml | 27 +++++++++++++++++++++ .github/workflows/check.yml | 10 ++++++++ .github/workflows/test.yml | 5 +--- 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 .github/actions/trigger-workflow/action.yml diff --git a/.github/actions/trigger-workflow/action.yml b/.github/actions/trigger-workflow/action.yml new file mode 100644 index 000000000..5705334d0 --- /dev/null +++ b/.github/actions/trigger-workflow/action.yml @@ -0,0 +1,27 @@ +name: 'Trigger Workflow' +description: 'Triggers another GitHub Actions workflow; useful for ensuring the workflow runs based off of the appropriate branch.' + +inputs: + token: + description: 'Personal Access Token (PAT) with permissions to trigger workflows' + required: true + workflowFileName: + description: 'The file name of the workflow to trigger' + required: true + workflowInputs: + description: 'Inputs for the triggered workflow in JSON format' + required: false + default: '{}' + +runs: + using: 'composite' + steps: + - name: Trigger another workflow + run: | + curl \ + -X POST \ + -H "Authorization: token ${{ inputs.token }}" \ + -H "Accept: application/vnd.github.v3+json" \ + https://api.github.com/repos/${{ github.repository }}/actions/workflows/${{ inputs.workflowFileName }}/dispatches \ + -d '{"ref":"${{ github.ref_name }}", "inputs": ${{ inputs.workflowInputs }}}' + shell: bash diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index cf566eec0..93baed645 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -45,3 +45,13 @@ jobs: working-directory: ./m1 run: cargo check + tigger-test: + needs: check + runs-on: ubuntu-latest + steps: + + - name: Trigger test + uses: ./github/actions/trigger-workflow + with: + workflowFileName: test.yml + token: ${{ secrets.CI_PAT }} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e60f5815d..978e826ad 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,10 +1,7 @@ name: Cargo Test on: - workflow_run: - workflows: ["Cargo Check"] - types: - - completed + workflow_dispatch: jobs: test: From 47c9f3050c4e90b11b4642e0cc38e6229907acc5 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 29 Nov 2023 16:06:07 -0800 Subject: [PATCH 10/58] fix: must checkout branch. --- .github/workflows/check.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 93baed645..9daff441e 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -50,6 +50,13 @@ jobs: runs-on: ubuntu-latest steps: + - name: Checkout Repository + uses: actions/checkout@v2 + with: + submodules: 'recursive' + token: ${{ secrets.CI_PAT }} + ref: ${{ github.event.inputs.branch }} + - name: Trigger test uses: ./github/actions/trigger-workflow with: From 9aefdfff2b9a6b67731220976c66d181799ca844 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 29 Nov 2023 16:13:21 -0800 Subject: [PATCH 11/58] fix: should be .github. --- .github/workflows/check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 9daff441e..5e575c8b0 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -58,7 +58,7 @@ jobs: ref: ${{ github.event.inputs.branch }} - name: Trigger test - uses: ./github/actions/trigger-workflow + uses: ./.github/actions/trigger-workflow with: workflowFileName: test.yml token: ${{ secrets.CI_PAT }} \ No newline at end of file From 92b1ccdbd41321f3cc37def96abdd4116b895d00 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 29 Nov 2023 16:54:05 -0800 Subject: [PATCH 12/58] fix: trying without the branch ref. --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 978e826ad..66f8fbdae 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,7 @@ jobs: with: submodules: 'recursive' token: ${{ secrets.CI_PAT }} - ref: ${{ github.event.inputs.branch }} + # ref: ${{ github.event.inputs.branch }} - name: Setup uses: ./.github/actions/setup-linux-x86_64 From e32d1c016833beb906960afccde41e6068c01175 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 29 Nov 2023 16:55:20 -0800 Subject: [PATCH 13/58] fix: trying with ref. --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 66f8fbdae..797aac081 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,7 @@ jobs: with: submodules: 'recursive' token: ${{ secrets.CI_PAT }} - # ref: ${{ github.event.inputs.branch }} + ref: ${{ github.ref }} - name: Setup uses: ./.github/actions/setup-linux-x86_64 From 074b35178e1a7ec02d9f41db549a09f3cf1cb000 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 29 Nov 2023 16:57:43 -0800 Subject: [PATCH 14/58] fix: restructuring ctl. --- movement-sdk/clis/movement/src/ctl/mod.rs | 1 + movement-sdk/clis/movement/src/ctl/services/mod.rs | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 movement-sdk/clis/movement/src/ctl/services/mod.rs diff --git a/movement-sdk/clis/movement/src/ctl/mod.rs b/movement-sdk/clis/movement/src/ctl/mod.rs index e69de29bb..dff86c7f0 100644 --- a/movement-sdk/clis/movement/src/ctl/mod.rs +++ b/movement-sdk/clis/movement/src/ctl/mod.rs @@ -0,0 +1 @@ +pub mod services; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/services/mod.rs b/movement-sdk/clis/movement/src/ctl/services/mod.rs new file mode 100644 index 000000000..c16607910 --- /dev/null +++ b/movement-sdk/clis/movement/src/ctl/services/mod.rs @@ -0,0 +1,2 @@ +pub mod m1; +pub mod m2; \ No newline at end of file From 70d369aa2bd39442e66e0a2f29c5a8a733ac553a Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 29 Nov 2023 17:06:50 -0800 Subject: [PATCH 15/58] chore: structure ctl. --- movement-sdk/clis/movement/src/ctl/ctl.rs | 12 +++++++++++- .../clis/movement/src/ctl/services/m1/mod.rs | 0 .../clis/movement/src/ctl/services/m2/mod.rs | 0 movement-sdk/clis/movement/src/ctl/services/mod.rs | 3 ++- .../clis/movement/src/ctl/services/service.rs | 14 ++++++++++++++ movement-sdk/clis/movement/src/ctl/start/mod.rs | 2 ++ movement-sdk/clis/movement/src/ctl/start/start.rs | 7 +++++++ movement-sdk/clis/movement/src/ctl/status/mod.rs | 2 ++ .../clis/movement/src/ctl/status/status.rs | 7 +++++++ movement-sdk/clis/movement/src/ctl/stop/mod.rs | 2 ++ movement-sdk/clis/movement/src/ctl/stop/stop.rs | 7 +++++++ 11 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 movement-sdk/clis/movement/src/ctl/services/m1/mod.rs create mode 100644 movement-sdk/clis/movement/src/ctl/services/m2/mod.rs create mode 100644 movement-sdk/clis/movement/src/ctl/services/service.rs create mode 100644 movement-sdk/clis/movement/src/ctl/start/start.rs create mode 100644 movement-sdk/clis/movement/src/ctl/status/status.rs create mode 100644 movement-sdk/clis/movement/src/ctl/stop/stop.rs diff --git a/movement-sdk/clis/movement/src/ctl/ctl.rs b/movement-sdk/clis/movement/src/ctl/ctl.rs index 7f6f32aaf..002248ff6 100644 --- a/movement-sdk/clis/movement/src/ctl/ctl.rs +++ b/movement-sdk/clis/movement/src/ctl/ctl.rs @@ -1,7 +1,17 @@ use async_trait::async_trait; use clap::Subcommand; +use super::{ + start::Start, + status::Status, + stop::Stop, +}; #[derive(Subcommand, Debug)] pub enum Ctl { - + #[clap(subcommand)] + Start(Start), + #[clap(subcommand)] + Status(Status), + #[clap(subcommand)] + Stop(Stop), } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/services/m1/mod.rs b/movement-sdk/clis/movement/src/ctl/services/m1/mod.rs new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/clis/movement/src/ctl/services/m2/mod.rs b/movement-sdk/clis/movement/src/ctl/services/m2/mod.rs new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/clis/movement/src/ctl/services/mod.rs b/movement-sdk/clis/movement/src/ctl/services/mod.rs index c16607910..fc9ae29a9 100644 --- a/movement-sdk/clis/movement/src/ctl/services/mod.rs +++ b/movement-sdk/clis/movement/src/ctl/services/mod.rs @@ -1,2 +1,3 @@ pub mod m1; -pub mod m2; \ No newline at end of file +pub mod m2; +pub mod service; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/services/service.rs b/movement-sdk/clis/movement/src/ctl/services/service.rs new file mode 100644 index 000000000..ffb7d8ba9 --- /dev/null +++ b/movement-sdk/clis/movement/src/ctl/services/service.rs @@ -0,0 +1,14 @@ +use async_trait::async_trait; + +#[async_trait] +pub trait Service { + + async fn get_name(&self) -> String; + + async fn start(&self) -> Result<(), anyhow::Error>; + + async fn stop(&self) -> Result<(), anyhow::Error>; + + async fn status(&self) -> Result<(), anyhow::Error>; + +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/start/mod.rs b/movement-sdk/clis/movement/src/ctl/start/mod.rs index e69de29bb..eb594da6f 100644 --- a/movement-sdk/clis/movement/src/ctl/start/mod.rs +++ b/movement-sdk/clis/movement/src/ctl/start/mod.rs @@ -0,0 +1,2 @@ +pub mod start; +pub use start::Start; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/start/start.rs b/movement-sdk/clis/movement/src/ctl/start/start.rs new file mode 100644 index 000000000..6a043ba63 --- /dev/null +++ b/movement-sdk/clis/movement/src/ctl/start/start.rs @@ -0,0 +1,7 @@ +use async_trait::async_trait; +use clap::Subcommand; + +#[derive(Subcommand, Debug)] +pub enum Start { + +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/status/mod.rs b/movement-sdk/clis/movement/src/ctl/status/mod.rs index e69de29bb..e6477ef19 100644 --- a/movement-sdk/clis/movement/src/ctl/status/mod.rs +++ b/movement-sdk/clis/movement/src/ctl/status/mod.rs @@ -0,0 +1,2 @@ +pub mod status; +pub use status::Status; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/status/status.rs b/movement-sdk/clis/movement/src/ctl/status/status.rs new file mode 100644 index 000000000..7238b9cb4 --- /dev/null +++ b/movement-sdk/clis/movement/src/ctl/status/status.rs @@ -0,0 +1,7 @@ +use async_trait::async_trait; +use clap::Subcommand; + +#[derive(Subcommand, Debug)] +pub enum Status { + +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/stop/mod.rs b/movement-sdk/clis/movement/src/ctl/stop/mod.rs index e69de29bb..d22e0571e 100644 --- a/movement-sdk/clis/movement/src/ctl/stop/mod.rs +++ b/movement-sdk/clis/movement/src/ctl/stop/mod.rs @@ -0,0 +1,2 @@ +pub mod stop; +pub use stop::Stop; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/stop/stop.rs b/movement-sdk/clis/movement/src/ctl/stop/stop.rs new file mode 100644 index 000000000..46d20eb19 --- /dev/null +++ b/movement-sdk/clis/movement/src/ctl/stop/stop.rs @@ -0,0 +1,7 @@ +use async_trait::async_trait; +use clap::Subcommand; + +#[derive(Subcommand, Debug)] +pub enum Stop { + +} \ No newline at end of file From 06bcc11a142803a8c6c97aa1baa336573c1a9d2c Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 29 Nov 2023 17:31:34 -0800 Subject: [PATCH 16/58] fix: structuring CLI. --- movement-sdk/Cargo.lock | 1 + movement-sdk/clis/movement/Cargo.toml | 3 +- .../clis/movement/src/ctl/services/m1/m1.rs | 55 +++++++++++++++++++ .../clis/movement/src/ctl/services/m1/mod.rs | 3 + .../movement/src/ctl/services/m1/proxy/mod.rs | 2 + .../src/ctl/services/m1/proxy/proxy.rs | 32 +++++++++++ .../src/ctl/services/m1/subnet/fuji.rs | 31 +++++++++++ .../src/ctl/services/m1/subnet/local.rs | 31 +++++++++++ .../src/ctl/services/m1/subnet/mod.rs | 6 ++ .../src/ctl/services/m1/subnet/subnet.rs | 55 +++++++++++++++++++ .../movement/src/ctl/services/mevm/mod.rs | 0 .../clis/movement/src/ctl/services/mod.rs | 4 +- 12 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 movement-sdk/clis/movement/src/ctl/services/m1/m1.rs create mode 100644 movement-sdk/clis/movement/src/ctl/services/m1/proxy/mod.rs create mode 100644 movement-sdk/clis/movement/src/ctl/services/m1/proxy/proxy.rs create mode 100644 movement-sdk/clis/movement/src/ctl/services/m1/subnet/fuji.rs create mode 100644 movement-sdk/clis/movement/src/ctl/services/m1/subnet/local.rs create mode 100644 movement-sdk/clis/movement/src/ctl/services/m1/subnet/mod.rs create mode 100644 movement-sdk/clis/movement/src/ctl/services/m1/subnet/subnet.rs create mode 100644 movement-sdk/clis/movement/src/ctl/services/mevm/mod.rs diff --git a/movement-sdk/Cargo.lock b/movement-sdk/Cargo.lock index 0efaf36b9..a9ee475c7 100644 --- a/movement-sdk/Cargo.lock +++ b/movement-sdk/Cargo.lock @@ -1387,6 +1387,7 @@ dependencies = [ "async-trait", "clap", "serde", + "tokio", ] [[package]] diff --git a/movement-sdk/clis/movement/Cargo.toml b/movement-sdk/clis/movement/Cargo.toml index c86b1cbaf..4999146d7 100644 --- a/movement-sdk/clis/movement/Cargo.toml +++ b/movement-sdk/clis/movement/Cargo.toml @@ -7,4 +7,5 @@ edition = "2021" async-trait = { workspace = true } anyhow = { workspace = true } serde = { workspace = true } -clap = { workspace = true } \ No newline at end of file +clap = { workspace = true } +tokio = { workspace = true} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/services/m1/m1.rs b/movement-sdk/clis/movement/src/ctl/services/m1/m1.rs new file mode 100644 index 000000000..71affe280 --- /dev/null +++ b/movement-sdk/clis/movement/src/ctl/services/m1/m1.rs @@ -0,0 +1,55 @@ +use async_trait::async_trait; +use super::super::Service; +use super::{ + proxy::M1SubnetProxyService, + subnet::M1SubnetService, +}; + + +#[derive(Debug)] +pub struct M1Service { + pub subnet: M1SubnetService, + pub proxy: M1SubnetProxyService, +} + +#[async_trait] +impl Service for M1Service { + + async fn get_name(&self) -> String { + "m1".to_string() + } + + async fn start(&self) -> Result<(), anyhow::Error> { + + tokio::try_join!( + self.subnet.start(), + self.proxy.start(), + )?; + + Ok(()) + + } + + async fn stop(&self) -> Result<(), anyhow::Error> { + + tokio::try_join!( + self.subnet.stop(), + self.proxy.stop(), + )?; + + Ok(()) + + } + + async fn status(&self) -> Result<(), anyhow::Error> { + + tokio::try_join!( + self.subnet.status(), + self.proxy.status(), + )?; + + Ok(()) + + } + +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/services/m1/mod.rs b/movement-sdk/clis/movement/src/ctl/services/m1/mod.rs index e69de29bb..f8bb6907b 100644 --- a/movement-sdk/clis/movement/src/ctl/services/m1/mod.rs +++ b/movement-sdk/clis/movement/src/ctl/services/m1/mod.rs @@ -0,0 +1,3 @@ +pub mod m1; +pub mod subnet; +pub mod proxy; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/services/m1/proxy/mod.rs b/movement-sdk/clis/movement/src/ctl/services/m1/proxy/mod.rs new file mode 100644 index 000000000..51e65465b --- /dev/null +++ b/movement-sdk/clis/movement/src/ctl/services/m1/proxy/mod.rs @@ -0,0 +1,2 @@ +pub mod proxy; +pub use proxy::M1SubnetProxyService; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/services/m1/proxy/proxy.rs b/movement-sdk/clis/movement/src/ctl/services/m1/proxy/proxy.rs new file mode 100644 index 000000000..a12dc5aa4 --- /dev/null +++ b/movement-sdk/clis/movement/src/ctl/services/m1/proxy/proxy.rs @@ -0,0 +1,32 @@ +use async_trait::async_trait; +use clap::Parser; +use super::super::super::Service; + +#[derive(Parser, Debug)] +pub struct M1SubnetProxyService { + +} + +#[async_trait] +impl Service for M1SubnetProxyService { + + async fn get_name(&self) -> String { + "m1-subnet-proxy".to_string() + } + + async fn start(&self) -> Result<(), anyhow::Error> { + unimplemented!(); + Ok(()) + } + + async fn stop(&self) -> Result<(), anyhow::Error> { + unimplemented!(); + Ok(()) + } + + async fn status(&self) -> Result<(), anyhow::Error> { + unimplemented!(); + Ok(()) + } + +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/services/m1/subnet/fuji.rs b/movement-sdk/clis/movement/src/ctl/services/m1/subnet/fuji.rs new file mode 100644 index 000000000..55d867756 --- /dev/null +++ b/movement-sdk/clis/movement/src/ctl/services/m1/subnet/fuji.rs @@ -0,0 +1,31 @@ +use async_trait::async_trait; +use super::super::super::Service; + +#[derive(Debug)] +pub struct M1SubnetFujiService { + +} + +#[async_trait] +impl Service for M1SubnetFujiService { + + async fn get_name(&self) -> String { + "m1-subnet-fuji".to_string() + } + + async fn start(&self) -> Result<(), anyhow::Error> { + unimplemented!(); + Ok(()) + } + + async fn stop(&self) -> Result<(), anyhow::Error> { + unimplemented!(); + Ok(()) + } + + async fn status(&self) -> Result<(), anyhow::Error> { + unimplemented!(); + Ok(()) + } + +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/services/m1/subnet/local.rs b/movement-sdk/clis/movement/src/ctl/services/m1/subnet/local.rs new file mode 100644 index 000000000..78fcc844e --- /dev/null +++ b/movement-sdk/clis/movement/src/ctl/services/m1/subnet/local.rs @@ -0,0 +1,31 @@ +use async_trait::async_trait; +use super::super::super::Service; + +#[derive(Debug)] +pub struct M1SubnetLocalService { + +} + +#[async_trait] +impl Service for M1SubnetLocalService { + + async fn get_name(&self) -> String { + "m1-subnet-local".to_string() + } + + async fn start(&self) -> Result<(), anyhow::Error> { + unimplemented!(); + Ok(()) + } + + async fn stop(&self) -> Result<(), anyhow::Error> { + unimplemented!(); + Ok(()) + } + + async fn status(&self) -> Result<(), anyhow::Error> { + unimplemented!(); + Ok(()) + } + +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/services/m1/subnet/mod.rs b/movement-sdk/clis/movement/src/ctl/services/m1/subnet/mod.rs new file mode 100644 index 000000000..6f1ab6733 --- /dev/null +++ b/movement-sdk/clis/movement/src/ctl/services/m1/subnet/mod.rs @@ -0,0 +1,6 @@ +pub mod subnet; +pub use subnet::M1SubnetService; +pub mod fuji; +pub use fuji::M1SubnetFujiService; +pub mod local; +pub use local::M1SubnetLocalService; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/services/m1/subnet/subnet.rs b/movement-sdk/clis/movement/src/ctl/services/m1/subnet/subnet.rs new file mode 100644 index 000000000..713bae36a --- /dev/null +++ b/movement-sdk/clis/movement/src/ctl/services/m1/subnet/subnet.rs @@ -0,0 +1,55 @@ +use async_trait::async_trait; +use super::super::super::Service; +use super::{ + M1SubnetFujiService, + M1SubnetLocalService +}; + +#[derive(Debug)] +pub enum M1SubnetService { + Local(M1SubnetLocalService), + Fuji(M1SubnetFujiService), +} + +#[async_trait] +impl Service for M1SubnetService { + + async fn get_name(&self) -> String { + // todo: this may need to be the name from the inner service + "m1-subnet".to_string() + } + + async fn start(&self) -> Result<(), anyhow::Error> { + match self { + M1SubnetService::Local(service) => service.start().await, + M1SubnetService::Fuji(service) => service.start().await, + } + } + + async fn stop(&self) -> Result<(), anyhow::Error> { + match self { + M1SubnetService::Local(service) => service.stop().await, + M1SubnetService::Fuji(service) => service.stop().await, + } + } + + async fn status(&self) -> Result<(), anyhow::Error> { + match self { + M1SubnetService::Local(service) => service.status().await, + M1SubnetService::Fuji(service) => service.status().await, + } + } + +} + +impl Into for M1SubnetLocalService { + fn into(self) -> M1SubnetService { + M1SubnetService::Local(self) + } +} + +impl Into for M1SubnetFujiService { + fn into(self) -> M1SubnetService { + M1SubnetService::Fuji(self) + } +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/services/mevm/mod.rs b/movement-sdk/clis/movement/src/ctl/services/mevm/mod.rs new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/clis/movement/src/ctl/services/mod.rs b/movement-sdk/clis/movement/src/ctl/services/mod.rs index fc9ae29a9..9f95d9ee6 100644 --- a/movement-sdk/clis/movement/src/ctl/services/mod.rs +++ b/movement-sdk/clis/movement/src/ctl/services/mod.rs @@ -1,3 +1,5 @@ pub mod m1; pub mod m2; -pub mod service; \ No newline at end of file +pub mod mevm; +pub mod service; +pub use service::Service; \ No newline at end of file From ef2ac8d6b455922f9a40a5b7c9e1f8bc6fee9fdf Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 08:10:08 -0800 Subject: [PATCH 17/58] chore: CLI structuring. --- .../clis/movement/src/manage/install/install.rs | 10 ++++++++++ movement-sdk/clis/movement/src/manage/install/mod.rs | 3 +++ .../clis/movement/src/manage/install/options.rs | 4 ++++ movement-sdk/clis/movement/src/manage/manage.rs | 7 +++++++ movement-sdk/clis/movement/src/manage/mod.rs | 3 +++ 5 files changed, 27 insertions(+) create mode 100644 movement-sdk/clis/movement/src/manage/install/install.rs create mode 100644 movement-sdk/clis/movement/src/manage/install/mod.rs create mode 100644 movement-sdk/clis/movement/src/manage/install/options.rs create mode 100644 movement-sdk/clis/movement/src/manage/manage.rs diff --git a/movement-sdk/clis/movement/src/manage/install/install.rs b/movement-sdk/clis/movement/src/manage/install/install.rs new file mode 100644 index 000000000..c351c73e0 --- /dev/null +++ b/movement-sdk/clis/movement/src/manage/install/install.rs @@ -0,0 +1,10 @@ +use async_trait::async_trait; +use clap::{Subcommand, Parser}; + +#[derive(Debug, Parser)] +pub struct All; + +#[derive(Subcommand, Debug)] +pub enum Install { + All(All) +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/manage/install/mod.rs b/movement-sdk/clis/movement/src/manage/install/mod.rs new file mode 100644 index 000000000..1e1ede31c --- /dev/null +++ b/movement-sdk/clis/movement/src/manage/install/mod.rs @@ -0,0 +1,3 @@ +pub mod install; +pub use install::Install; +pub mod options; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/manage/install/options.rs b/movement-sdk/clis/movement/src/manage/install/options.rs new file mode 100644 index 000000000..3082edea0 --- /dev/null +++ b/movement-sdk/clis/movement/src/manage/install/options.rs @@ -0,0 +1,4 @@ +#[derive(Debug, StructOpt)] +pub struct Options { + +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/manage/manage.rs b/movement-sdk/clis/movement/src/manage/manage.rs new file mode 100644 index 000000000..446a45990 --- /dev/null +++ b/movement-sdk/clis/movement/src/manage/manage.rs @@ -0,0 +1,7 @@ +use async_trait::async_trait; +use clap::Subcommand; + +#[derive(Subcommand, Debug)] +pub enum Manage { + +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/manage/mod.rs b/movement-sdk/clis/movement/src/manage/mod.rs index e69de29bb..cec1da597 100644 --- a/movement-sdk/clis/movement/src/manage/mod.rs +++ b/movement-sdk/clis/movement/src/manage/mod.rs @@ -0,0 +1,3 @@ +pub mod install; +pub mod manage; +pub use manage::Manage; \ No newline at end of file From 348c7bc754ab4bd2a3f6ae5443296f5fa8dd8dce Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 08:14:24 -0800 Subject: [PATCH 18/58] fix: registering workflow. --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 797aac081..afb27512e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,6 +2,7 @@ name: Cargo Test on: workflow_dispatch: + push: jobs: test: From b70b26936ce672522e1d9763491d8e782bda134f Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 08:46:57 -0800 Subject: [PATCH 19/58] fix: dev --- .github/workflows/check.yml | 4 +++- .github/workflows/test.yml | 1 - README.md | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 5e575c8b0..137f14d69 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -61,4 +61,6 @@ jobs: uses: ./.github/actions/trigger-workflow with: workflowFileName: test.yml - token: ${{ secrets.CI_PAT }} \ No newline at end of file + token: ${{ secrets.CI_PAT }} + + diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index afb27512e..797aac081 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,7 +2,6 @@ name: Cargo Test on: workflow_dispatch: - push: jobs: test: diff --git a/README.md b/README.md index 0571fbf24..25bd72d49 100644 --- a/README.md +++ b/README.md @@ -69,5 +69,4 @@ Please submit and review/comment on issues before contributing. Review [CONTRIBU ## License -This project is licensed under the BSD-3-Clause License - see the [LICENSE](LICENSE) file for details. - +This project is licensed under the BSD-3-Clause License - see the [LICENSE](LICENSE) file for details. \ No newline at end of file From baf7a6cf3da425adde06878f11fff26d3bcdc939 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 08:59:56 -0800 Subject: [PATCH 20/58] fix: checking dev workflow. --- .github/workflows/check.yml | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 137f14d69..6ade18928 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -20,6 +20,12 @@ jobs: submodules: 'recursive' token: ${{ secrets.CI_PAT }} + - name: Trigger test + uses: ./.github/actions/trigger-workflow + with: + workflowFileName: test.yml + token: ${{ secrets.CI_PAT }} + - name: Setup uses: ./.github/actions/setup-linux-x86_64 @@ -45,22 +51,6 @@ jobs: working-directory: ./m1 run: cargo check - tigger-test: - needs: check - runs-on: ubuntu-latest - steps: - - - name: Checkout Repository - uses: actions/checkout@v2 - with: - submodules: 'recursive' - token: ${{ secrets.CI_PAT }} - ref: ${{ github.event.inputs.branch }} - - - name: Trigger test - uses: ./.github/actions/trigger-workflow - with: - workflowFileName: test.yml - token: ${{ secrets.CI_PAT }} + From 3493a8dcaff9a299999518f6b775bc264b03bfe6 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 09:50:37 -0800 Subject: [PATCH 21/58] fix: testing new trigger-workflow action, hopefully we can see the issue with the current github endpoint ping. --- .github/actions/trigger-workflow/action.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/actions/trigger-workflow/action.yml b/.github/actions/trigger-workflow/action.yml index 5705334d0..b0be4ac12 100644 --- a/.github/actions/trigger-workflow/action.yml +++ b/.github/actions/trigger-workflow/action.yml @@ -18,10 +18,13 @@ runs: steps: - name: Trigger another workflow run: | - curl \ - -X POST \ - -H "Authorization: token ${{ inputs.token }}" \ - -H "Accept: application/vnd.github.v3+json" \ - https://api.github.com/repos/${{ github.repository }}/actions/workflows/${{ inputs.workflowFileName }}/dispatches \ - -d '{"ref":"${{ github.ref_name }}", "inputs": ${{ inputs.workflowInputs }}}' - shell: bash + echo "Repository: ${{ github.repository }}" + echo "Workflow File Name: ${{ inputs.workflowFileName }}" + echo "Ref: ${{ github.ref_name }}" + echo "Inputs: ${{ inputs.workflowInputs }}" + + curl_command="curl -X POST -H \"Authorization: token ${{ inputs.token }}\" -H \"Accept: application/vnd.github.v3+json\" https://api.github.com/repos/${{ github.repository }}/actions/workflows/${{ inputs.workflowFileName }}/dispatches -d '{\"ref\":\"${{ github.ref_name }}\", \"inputs\": ${{ inputs.workflowInputs }}}'" + echo "Executing command: $curl_command" + + eval $curl_command + shell: bash \ No newline at end of file From 9256482490c1fd705640456f197f16ea26a1404a Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 10:15:39 -0800 Subject: [PATCH 22/58] fix: dev workflow with gh cli. --- .github/actions/trigger-workflow/action.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/actions/trigger-workflow/action.yml b/.github/actions/trigger-workflow/action.yml index b0be4ac12..c750cc4af 100644 --- a/.github/actions/trigger-workflow/action.yml +++ b/.github/actions/trigger-workflow/action.yml @@ -16,15 +16,14 @@ inputs: runs: using: 'composite' steps: + - name: Trigger another workflow run: | + echo ${{ inputs.token }} | gh auth login --with-token echo "Repository: ${{ github.repository }}" echo "Workflow File Name: ${{ inputs.workflowFileName }}" echo "Ref: ${{ github.ref_name }}" echo "Inputs: ${{ inputs.workflowInputs }}" - curl_command="curl -X POST -H \"Authorization: token ${{ inputs.token }}\" -H \"Accept: application/vnd.github.v3+json\" https://api.github.com/repos/${{ github.repository }}/actions/workflows/${{ inputs.workflowFileName }}/dispatches -d '{\"ref\":\"${{ github.ref_name }}\", \"inputs\": ${{ inputs.workflowInputs }}}'" - echo "Executing command: $curl_command" - - eval $curl_command - shell: bash \ No newline at end of file + gh workflow run ${{ inputs.workflowFileName }} --ref ${{ github.ref_name }} -f ${{ inputs.workflowInputs }} + shell: bash From b623c00f20a6b9a97fd8b3fbe9312cbbf81425a4 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 10:17:36 -0800 Subject: [PATCH 23/58] fix: no inputs. --- .github/actions/trigger-workflow/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/trigger-workflow/action.yml b/.github/actions/trigger-workflow/action.yml index c750cc4af..8861c8a35 100644 --- a/.github/actions/trigger-workflow/action.yml +++ b/.github/actions/trigger-workflow/action.yml @@ -25,5 +25,5 @@ runs: echo "Ref: ${{ github.ref_name }}" echo "Inputs: ${{ inputs.workflowInputs }}" - gh workflow run ${{ inputs.workflowFileName }} --ref ${{ github.ref_name }} -f ${{ inputs.workflowInputs }} + gh workflow run ${{ inputs.workflowFileName }} --ref ${{ github.ref_name }} shell: bash From bd3a3a6a653d772a2b53769c5e56a58da6348ec8 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 10:28:06 -0800 Subject: [PATCH 24/58] fix: branch-based. --- .github/actions/trigger-workflow/action.yml | 1 + .github/workflows/check.yml | 12 ++++++------ .github/workflows/coverage.yml | 6 +----- .github/workflows/release.yml | 8 +------- .github/workflows/test.yml | 8 +++++++- 5 files changed, 16 insertions(+), 19 deletions(-) diff --git a/.github/actions/trigger-workflow/action.yml b/.github/actions/trigger-workflow/action.yml index 8861c8a35..8c97d84fd 100644 --- a/.github/actions/trigger-workflow/action.yml +++ b/.github/actions/trigger-workflow/action.yml @@ -17,6 +17,7 @@ runs: using: 'composite' steps: + # for some reason this only works with gh-cli - name: Trigger another workflow run: | echo ${{ inputs.token }} | gh auth login --with-token diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 6ade18928..b927638b1 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -20,12 +20,6 @@ jobs: submodules: 'recursive' token: ${{ secrets.CI_PAT }} - - name: Trigger test - uses: ./.github/actions/trigger-workflow - with: - workflowFileName: test.yml - token: ${{ secrets.CI_PAT }} - - name: Setup uses: ./.github/actions/setup-linux-x86_64 @@ -51,6 +45,12 @@ jobs: working-directory: ./m1 run: cargo check + - name: Trigger test + uses: ./.github/actions/trigger-workflow + with: + workflowFileName: test.yml + token: ${{ secrets.CI_PAT }} + diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index b2a7c84a0..854bba7dc 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,16 +1,12 @@ name: Code Coverage on: - workflow_run: - workflows: ["Cargo Check"] - types: - - completed + workflow_dispatch: jobs: coverage: runs-on: labels: movement-runner - if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Checkout Repository uses: actions/checkout@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4d988c32e..cf1d593b6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,13 +3,7 @@ name: Release permissions: write-all on: - # push: - # branches: - # - main - workflow_run: - workflows: ["Cargo Test"] - types: - - completed + workflow_dispatch: jobs: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 797aac081..55d241f69 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,7 +7,7 @@ jobs: test: runs-on: labels: movement-runner - if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: - name: Checkout Repository @@ -34,3 +34,9 @@ jobs: sed -i 's/badge\/tests-[a-zA-Z]*/badge\/tests-Failing-red/g' README.md fi if: ${{ always() }} + + - name: Trigger release + uses: ./.github/actions/trigger-workflow + with: + workflowFileName: release.yml + token: ${{ secrets.CI_PAT }} From fc373282b42902d0a926f343bf028d8cd7e4d5f2 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 10:42:07 -0800 Subject: [PATCH 25/58] fix: protoc test. --- .github/actions/setup-linux-x86_64/action.yml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/actions/setup-linux-x86_64/action.yml b/.github/actions/setup-linux-x86_64/action.yml index 529fd3c12..400e1f5f6 100644 --- a/.github/actions/setup-linux-x86_64/action.yml +++ b/.github/actions/setup-linux-x86_64/action.yml @@ -8,9 +8,20 @@ runs: - name: Install build essentials run: | sudo apt-get update - sudo apt-get install -y build-essential lld libpq-dev - # Install Protocol Buffers Compiler (protoc) - sudo apt-get install -y protobuf-compiler + sudo apt-get install -y build-essential lld libpq-dev unzip + + # Define the desired version of protoc + PROTOC_VERSION="3.15.8" + PROTOC_ZIP="protoc-${PROTOC_VERSION}-linux-x86_64.zip" + + # Download and install protoc + curl -OL "https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/${PROTOC_ZIP}" + sudo unzip -o $PROTOC_ZIP -d /usr/local bin/protoc + sudo unzip -o $PROTOC_ZIP -d /usr/local 'include/*' + rm -f $PROTOC_ZIP + + # Verify the installation + protoc --version shell: bash - name: Set up Rust From 0bdad00f59ec8c9cb64092e3642e7ff906483a78 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 13:41:11 -0800 Subject: [PATCH 26/58] fix: use arduino/setup-protoc --- .github/actions/setup-linux-x86_64/action.yml | 16 +++------------- .github/workflows/check.yml | 7 +++++++ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/.github/actions/setup-linux-x86_64/action.yml b/.github/actions/setup-linux-x86_64/action.yml index 400e1f5f6..5bdcf8a3d 100644 --- a/.github/actions/setup-linux-x86_64/action.yml +++ b/.github/actions/setup-linux-x86_64/action.yml @@ -9,20 +9,10 @@ runs: run: | sudo apt-get update sudo apt-get install -y build-essential lld libpq-dev unzip - - # Define the desired version of protoc - PROTOC_VERSION="3.15.8" - PROTOC_ZIP="protoc-${PROTOC_VERSION}-linux-x86_64.zip" - - # Download and install protoc - curl -OL "https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/${PROTOC_ZIP}" - sudo unzip -o $PROTOC_ZIP -d /usr/local bin/protoc - sudo unzip -o $PROTOC_ZIP -d /usr/local 'include/*' - rm -f $PROTOC_ZIP - - # Verify the installation - protoc --version shell: bash + + - name: Install Protoc + uses: arduino/setup-protoc@v2 - name: Set up Rust uses: actions-rs/toolchain@v1 diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index b927638b1..2155dd833 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -20,6 +20,13 @@ jobs: submodules: 'recursive' token: ${{ secrets.CI_PAT }} + # for testing purposes + - name: Trigger test + uses: ./.github/actions/trigger-workflow + with: + workflowFileName: test.yml + token: ${{ secrets.CI_PAT }} + - name: Setup uses: ./.github/actions/setup-linux-x86_64 From b67558842fc0fdf2c72639cb561dd7a0cbc091d3 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 13:54:04 -0800 Subject: [PATCH 27/58] fix: setting up movement dir. --- movement-sdk/Cargo.lock | 43 +++++++++++++++++++ movement-sdk/Cargo.toml | 1 + movement-sdk/clis/movement/Cargo.toml | 3 +- movement-sdk/clis/movement/src/common/mod.rs | 3 +- .../movement/src/common/movement_dir/mod.rs | 1 + .../src/common/movement_dir/movement_dir.rs | 35 +++++++++++++++ 6 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 movement-sdk/clis/movement/src/common/movement_dir/mod.rs create mode 100644 movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs diff --git a/movement-sdk/Cargo.lock b/movement-sdk/Cargo.lock index a9ee475c7..fab4efaf6 100644 --- a/movement-sdk/Cargo.lock +++ b/movement-sdk/Cargo.lock @@ -660,6 +660,26 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "displaydoc" version = "0.2.4" @@ -1313,6 +1333,17 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall", +] + [[package]] name = "linux-raw-sys" version = "0.4.11" @@ -1386,6 +1417,7 @@ dependencies = [ "anyhow", "async-trait", "clap", + "dirs", "serde", "tokio", ] @@ -1878,6 +1910,17 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.10.2" diff --git a/movement-sdk/Cargo.toml b/movement-sdk/Cargo.toml index df95c6d32..dda2495d2 100644 --- a/movement-sdk/Cargo.toml +++ b/movement-sdk/Cargo.toml @@ -27,6 +27,7 @@ tokio = { version = "1.21.0", features = ["full"] } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["raw_value"] } ctor = "0.2.5" +dirs = "3.0.2" # todo: bump clap to most recent version clap = { version = "3.2.23", features = ["derive", "env", "suggestions"] } \ No newline at end of file diff --git a/movement-sdk/clis/movement/Cargo.toml b/movement-sdk/clis/movement/Cargo.toml index 4999146d7..590a6a230 100644 --- a/movement-sdk/clis/movement/Cargo.toml +++ b/movement-sdk/clis/movement/Cargo.toml @@ -8,4 +8,5 @@ async-trait = { workspace = true } anyhow = { workspace = true } serde = { workspace = true } clap = { workspace = true } -tokio = { workspace = true} \ No newline at end of file +tokio = { workspace = true} +dirs = { workspace = true } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/mod.rs b/movement-sdk/clis/movement/src/common/mod.rs index 5d863fb78..4a205c979 100644 --- a/movement-sdk/clis/movement/src/common/mod.rs +++ b/movement-sdk/clis/movement/src/common/mod.rs @@ -1 +1,2 @@ -pub mod cli; \ No newline at end of file +pub mod cli; +pub mod movement_dir; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_dir/mod.rs b/movement-sdk/clis/movement/src/common/movement_dir/mod.rs new file mode 100644 index 000000000..435a86577 --- /dev/null +++ b/movement-sdk/clis/movement/src/common/movement_dir/mod.rs @@ -0,0 +1 @@ +pub mod movement_dir; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs b/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs new file mode 100644 index 000000000..2b8ff190d --- /dev/null +++ b/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs @@ -0,0 +1,35 @@ +pub struct MovementDir { + pub path: PathBuf, +} + +impl MovementDir { + pub fn new(path: PathBuf) -> Self { + Self { path } + } + + pub fn path(&self) -> &PathBuf { + &self.path + } + + pub fn path_str(&self) -> &str { + self.path.to_str().unwrap() + } + + pub fn path_str_with_trailing_slash(&self) -> String { + format!("{}/", self.path_str()) + } + + pub fn path_str_without_trailing_slash(&self) -> String { + self.path_str().to_string() + } + +} + +impl Default for MovementDir { + fn default() -> Self { + Self { + // ! default path is $HOME/.movement + path : + } + } +} From 59224a27da25ef92f878b874ff72245339e8004d Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 13:55:21 -0800 Subject: [PATCH 28/58] fix: protoc versioning changed, need to updated script perhaps. --- .github/actions/setup-linux-x86_64/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/setup-linux-x86_64/action.yml b/.github/actions/setup-linux-x86_64/action.yml index 5bdcf8a3d..e8dd84dfe 100644 --- a/.github/actions/setup-linux-x86_64/action.yml +++ b/.github/actions/setup-linux-x86_64/action.yml @@ -13,6 +13,7 @@ runs: - name: Install Protoc uses: arduino/setup-protoc@v2 + version: '3.15.8' - name: Set up Rust uses: actions-rs/toolchain@v1 From 41f0fa48d2cff52d341b7f01ec200e6c076d30ba Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 13:59:56 -0800 Subject: [PATCH 29/58] fix: should be version 3.15.8 --- .github/actions/setup-linux-x86_64/action.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/setup-linux-x86_64/action.yml b/.github/actions/setup-linux-x86_64/action.yml index e8dd84dfe..2a1ea56e9 100644 --- a/.github/actions/setup-linux-x86_64/action.yml +++ b/.github/actions/setup-linux-x86_64/action.yml @@ -13,7 +13,8 @@ runs: - name: Install Protoc uses: arduino/setup-protoc@v2 - version: '3.15.8' + with: + version: '3.15.8' - name: Set up Rust uses: actions-rs/toolchain@v1 From 35bfd3a518ab444fef8c7ebaf78373367b91a6a1 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 14:04:55 -0800 Subject: [PATCH 30/58] fix: try allowing variable installation. --- .github/actions/setup-linux-x86_64/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup-linux-x86_64/action.yml b/.github/actions/setup-linux-x86_64/action.yml index 2a1ea56e9..26fe1d1c2 100644 --- a/.github/actions/setup-linux-x86_64/action.yml +++ b/.github/actions/setup-linux-x86_64/action.yml @@ -14,7 +14,7 @@ runs: - name: Install Protoc uses: arduino/setup-protoc@v2 with: - version: '3.15.8' + version: '3.15.x' - name: Set up Rust uses: actions-rs/toolchain@v1 From 18f52a800438ff83a03f7e8cb462c0fd09e64e3d Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 14:14:47 -0800 Subject: [PATCH 31/58] feat: m1 manifest. --- .../src/common/movement_dir/movement_dir.rs | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs b/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs index 2b8ff190d..6ce704b7d 100644 --- a/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs +++ b/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs @@ -1,5 +1,44 @@ +use std::path::PathBuf; + +#[derive(Debug, Clone)] +pub struct M1Manifest { + pub m1_source : Option, + pub subnet_binary : Option, + pub proxy_binary : Option +} + +impl M1Manifest { + pub fn new(m1_source : Option, subnet_binary : Option, proxy_binary : Option) -> Self { + Self { + m1_source, + subnet_binary, + proxy_binary + } + } +} + +impl Default for M1Manifest { + fn default() -> Self { + Self { + m1_source : None, + subnet_binary : None, + proxy_binary : None + } + } +} + + +#[derive(Debug, Clone)] +pub struct MovementDirManifest { + pub movement_dir : PathBuf, + pub movement_binary : PathBuf, + pub m1 : Option +} + +#[derive(Debug, Clone)] pub struct MovementDir { pub path: PathBuf, + pub manifest : MovementDirManifest } impl MovementDir { @@ -32,4 +71,4 @@ impl Default for MovementDir { path : } } -} +} \ No newline at end of file From 2608f2709b93bdc78133bbdba1621d13b94acee5 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 14:15:42 -0800 Subject: [PATCH 32/58] fix: trying protoc version with double-quotes. --- .github/actions/setup-linux-x86_64/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup-linux-x86_64/action.yml b/.github/actions/setup-linux-x86_64/action.yml index 26fe1d1c2..c1567e0a2 100644 --- a/.github/actions/setup-linux-x86_64/action.yml +++ b/.github/actions/setup-linux-x86_64/action.yml @@ -14,7 +14,7 @@ runs: - name: Install Protoc uses: arduino/setup-protoc@v2 with: - version: '3.15.x' + version: "3.20" - name: Set up Rust uses: actions-rs/toolchain@v1 From 20f14ec34a9b7f03b8e399c4a1e917dc06627dec Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 14:23:47 -0800 Subject: [PATCH 33/58] feat: .movement --- .../src/common/movement_dir/movement_dir.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs b/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs index 6ce704b7d..b1ce4855c 100644 --- a/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs +++ b/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs @@ -1,6 +1,7 @@ use std::path::PathBuf; +use serde::{Serialize, Deserialize}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct M1Manifest { pub m1_source : Option, pub subnet_binary : Option, @@ -28,7 +29,7 @@ impl Default for M1Manifest { } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct MovementDirManifest { pub movement_dir : PathBuf, pub movement_binary : PathBuf, @@ -42,6 +43,8 @@ pub struct MovementDir { } impl MovementDir { + + // basic operations pub fn new(path: PathBuf) -> Self { Self { path } } @@ -62,6 +65,13 @@ impl MovementDir { self.path_str().to_string() } + // m1 assets + pub fn write_m1_source(&self, m1_source : PathBuf) -> Result<(), std::io::Error> { + let mut manifest = self.manifest.clone(); + manifest.m1 = Some(M1Manifest::new(Some(m1_source), None, None)); + self.write_manifest(manifest) + } + } impl Default for MovementDir { From a660cc0847bbbd8fd3ad0a45a29d74279814e960 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 30 Nov 2023 14:24:18 -0800 Subject: [PATCH 34/58] feat: trying with earlier version of setup-protoc. --- .github/actions/setup-linux-x86_64/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/setup-linux-x86_64/action.yml b/.github/actions/setup-linux-x86_64/action.yml index c1567e0a2..6c0170b5b 100644 --- a/.github/actions/setup-linux-x86_64/action.yml +++ b/.github/actions/setup-linux-x86_64/action.yml @@ -12,9 +12,9 @@ runs: shell: bash - name: Install Protoc - uses: arduino/setup-protoc@v2 + uses: arduino/setup-protoc@v1.3.0 with: - version: "3.20" + version: "3.20.1" - name: Set up Rust uses: actions-rs/toolchain@v1 From 45dfd2032b39027a1eea589f7d96c1dba5256d92 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 1 Dec 2023 04:38:25 -0800 Subject: [PATCH 35/58] feat: improvements to movement dir. --- movement-sdk/Cargo.lock | 283 ++++++++++++++++++ movement-sdk/Cargo.toml | 3 + movement-sdk/clis/movement/Cargo.toml | 5 +- movement-sdk/clis/movement/src/common/mod.rs | 5 +- .../src/common/movement_artifacts/m1.rs | 9 + .../src/common/movement_artifacts/mod.rs | 3 + .../movement_artifacts/movement_artifacts.rs | 10 + .../common/movement_artifacts/movement_cli.rs | 6 + .../movement/src/common/movement_dir/m1.rs | 55 ++++ .../src/common/movement_dir/manifest.rs | 38 +++ .../movement/src/common/movement_dir/mod.rs | 4 +- .../src/common/movement_dir/movement_dir.rs | 74 +---- .../src/common/movement_releases/m1.rs | 109 +++++++ .../src/common/movement_releases/mod.rs | 7 + .../common/movement_releases/movement_cli.rs | 12 + .../movement_releases/movement_releases.rs | 129 ++++++++ .../clis/movement/src/common/util/file.rs | 0 .../clis/movement/src/common/util/mod.rs | 4 + .../clis/movement/src/common/util/util.rs | 0 .../clis/movement/src/common/util/version.rs | 8 + .../movement/src/manage/install/options.rs | 2 +- 21 files changed, 693 insertions(+), 73 deletions(-) create mode 100644 movement-sdk/clis/movement/src/common/movement_artifacts/m1.rs create mode 100644 movement-sdk/clis/movement/src/common/movement_artifacts/mod.rs create mode 100644 movement-sdk/clis/movement/src/common/movement_artifacts/movement_artifacts.rs create mode 100644 movement-sdk/clis/movement/src/common/movement_artifacts/movement_cli.rs create mode 100644 movement-sdk/clis/movement/src/common/movement_dir/m1.rs create mode 100644 movement-sdk/clis/movement/src/common/movement_dir/manifest.rs create mode 100644 movement-sdk/clis/movement/src/common/movement_releases/m1.rs create mode 100644 movement-sdk/clis/movement/src/common/movement_releases/mod.rs create mode 100644 movement-sdk/clis/movement/src/common/movement_releases/movement_cli.rs create mode 100644 movement-sdk/clis/movement/src/common/movement_releases/movement_releases.rs create mode 100644 movement-sdk/clis/movement/src/common/util/file.rs create mode 100644 movement-sdk/clis/movement/src/common/util/mod.rs create mode 100644 movement-sdk/clis/movement/src/common/util/util.rs create mode 100644 movement-sdk/clis/movement/src/common/util/version.rs diff --git a/movement-sdk/Cargo.lock b/movement-sdk/Cargo.lock index fab4efaf6..cddcb3167 100644 --- a/movement-sdk/Cargo.lock +++ b/movement-sdk/Cargo.lock @@ -499,6 +499,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -730,6 +740,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -865,6 +884,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.0" @@ -1151,6 +1185,19 @@ dependencies = [ "tokio-io-timeout", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.58" @@ -1250,6 +1297,12 @@ dependencies = [ "serde", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "itertools" version = "0.10.5" @@ -1418,7 +1471,10 @@ dependencies = [ "async-trait", "clap", "dirs", + "reqwest", + "semver", "serde", + "tempfile", "tokio", ] @@ -1431,6 +1487,24 @@ dependencies = [ "serde", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nom" version = "7.1.3" @@ -1591,6 +1665,50 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "openssl" +version = "0.10.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" +dependencies = [ + "bitflags 2.4.1", + "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.38", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "os_str_bytes" version = "6.6.1" @@ -1723,6 +1841,12 @@ dependencies = [ "spki", ] +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + [[package]] name = "powerfmt" version = "0.2.0" @@ -1950,6 +2074,46 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "reqwest" +version = "0.11.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +dependencies = [ + "base64 0.21.5", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -2184,6 +2348,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -2214,11 +2387,37 @@ dependencies = [ "zeroize", ] +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +dependencies = [ + "serde", +] [[package]] name = "serde" @@ -2251,6 +2450,18 @@ dependencies = [ "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_with" version = "3.4.0" @@ -2470,6 +2681,27 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" @@ -2626,6 +2858,16 @@ dependencies = [ "syn 2.0.38", ] +[[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-stream" version = "0.1.14" @@ -2866,6 +3108,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" @@ -2922,6 +3170,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.88" @@ -2951,6 +3211,19 @@ version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" +[[package]] +name = "wasm-streams" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.65" @@ -3076,6 +3349,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys", +] + [[package]] name = "wyz" version = "0.5.1" diff --git a/movement-sdk/Cargo.toml b/movement-sdk/Cargo.toml index dda2495d2..010f716ac 100644 --- a/movement-sdk/Cargo.toml +++ b/movement-sdk/Cargo.toml @@ -28,6 +28,9 @@ serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["raw_value"] } ctor = "0.2.5" dirs = "3.0.2" +reqwest = { version = "0.11.6", features = ["json", "stream"] } +tempfile = "3.2.0" +semver = { version = "1.0.5", features = ["serde"] } # todo: bump clap to most recent version clap = { version = "3.2.23", features = ["derive", "env", "suggestions"] } \ No newline at end of file diff --git a/movement-sdk/clis/movement/Cargo.toml b/movement-sdk/clis/movement/Cargo.toml index 590a6a230..ac7f5bd2d 100644 --- a/movement-sdk/clis/movement/Cargo.toml +++ b/movement-sdk/clis/movement/Cargo.toml @@ -9,4 +9,7 @@ anyhow = { workspace = true } serde = { workspace = true } clap = { workspace = true } tokio = { workspace = true} -dirs = { workspace = true } \ No newline at end of file +dirs = { workspace = true } +reqwest = { workspace = true } +tempfile = { workspace = true } +semver = { workspace = true } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/mod.rs b/movement-sdk/clis/movement/src/common/mod.rs index 4a205c979..a68295b9b 100644 --- a/movement-sdk/clis/movement/src/common/mod.rs +++ b/movement-sdk/clis/movement/src/common/mod.rs @@ -1,2 +1,5 @@ pub mod cli; -pub mod movement_dir; \ No newline at end of file +pub mod movement_artifacts; +pub mod movement_releases; +pub mod movement_dir; +pub mod util; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_artifacts/m1.rs b/movement-sdk/clis/movement/src/common/movement_artifacts/m1.rs new file mode 100644 index 000000000..edd5b81c8 --- /dev/null +++ b/movement-sdk/clis/movement/src/common/movement_artifacts/m1.rs @@ -0,0 +1,9 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum M1Artifact { + M1Source, + M1SourceWithSubmodules, + M1SubnetBinary, + M1SubnetRequestProxySource +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_artifacts/mod.rs b/movement-sdk/clis/movement/src/common/movement_artifacts/mod.rs new file mode 100644 index 000000000..b2f7c8744 --- /dev/null +++ b/movement-sdk/clis/movement/src/common/movement_artifacts/mod.rs @@ -0,0 +1,3 @@ +pub mod movement_artifacts; +pub mod m1; +pub mod movement_cli; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_artifacts/movement_artifacts.rs b/movement-sdk/clis/movement/src/common/movement_artifacts/movement_artifacts.rs new file mode 100644 index 000000000..8e511ce0c --- /dev/null +++ b/movement-sdk/clis/movement/src/common/movement_artifacts/movement_artifacts.rs @@ -0,0 +1,10 @@ +use serde::{Serialize, Deserialize}; +use super::movement_cli::MovementCliArtifact; +use super::m1::M1Artifact; + + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum MovementArtifact { + Movement(MovementCliArtifact), + M1Artifact(M1Artifact) +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_artifacts/movement_cli.rs b/movement-sdk/clis/movement/src/common/movement_artifacts/movement_cli.rs new file mode 100644 index 000000000..66d471838 --- /dev/null +++ b/movement-sdk/clis/movement/src/common/movement_artifacts/movement_cli.rs @@ -0,0 +1,6 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum MovementCliArtifact { + MovementBinary +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_dir/m1.rs b/movement-sdk/clis/movement/src/common/movement_dir/m1.rs new file mode 100644 index 000000000..78c54526f --- /dev/null +++ b/movement-sdk/clis/movement/src/common/movement_dir/m1.rs @@ -0,0 +1,55 @@ +use std::path::PathBuf; +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct M1Manifest { + pub m1_source : Option, + pub m1_subnet_binary : Option, + pub m1_proxy_binary : Option +} + +impl M1Manifest { + + pub fn new(m1_source : Option, m1_subnet_binary : Option, m1_proxy_binary : Option) -> Self { + Self { + m1_source, + m1_subnet_binary, + m1_proxy_binary + } + } + + pub fn register_m1_source_path(&mut self, m1_source : PathBuf) { + self.m1_source = Some(m1_source); + } + + pub fn remove_m1_source_path(&mut self) { + self.m1_source = None; + } + + pub fn register_m1_subnet_binary_path(&mut self, subnet_binary : PathBuf) { + self.m1_subnet_binary = Some(subnet_binary); + } + + pub fn remove_m1_subnet_binary_path(&mut self) { + self.m1_subnet_binary = None; + } + + pub fn register_m1_proxy_binary_path(&mut self, proxy_binary : PathBuf) { + self.m1_proxy_binary = Some(proxy_binary); + } + + pub fn remove_m1_proxy_path(&mut self) { + self.m1_proxy_binary = None; + } + +} + +impl Default for M1Manifest { + fn default() -> Self { + Self { + m1_source: None, + m1_subnet_binary: None, + m1_proxy_binary: None + } + } +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_dir/manifest.rs b/movement-sdk/clis/movement/src/common/movement_dir/manifest.rs new file mode 100644 index 000000000..c301386b3 --- /dev/null +++ b/movement-sdk/clis/movement/src/common/movement_dir/manifest.rs @@ -0,0 +1,38 @@ +use std::path::PathBuf; +use serde::{Serialize, Deserialize}; +use super::m1::M1Manifest; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MovementDirManifest { + pub movement_dir : Option, + pub movement_binary : Option, + pub m1 : Option +} + +impl MovementDirManifest { + + pub fn new(movement_dir : Option, movement_binary : Option, m1 : Option) -> Self { + Self { + movement_dir, + movement_binary, + m1 + } + } + + pub fn register_movement_dir_path(&mut self, movement_dir : PathBuf) { + self.movement_dir = Some(movement_dir); + } + + pub fn remove_movement_dir_path(&mut self) { + self.movement_dir = None; + } + + pub fn register_movement_binary_path(&mut self, movement_binary : PathBuf) { + self.movement_binary = Some(movement_binary); + } + + pub fn remove_movement_binary_path(&mut self) { + self.movement_binary = None; + } + +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_dir/mod.rs b/movement-sdk/clis/movement/src/common/movement_dir/mod.rs index 435a86577..231f8ed50 100644 --- a/movement-sdk/clis/movement/src/common/movement_dir/mod.rs +++ b/movement-sdk/clis/movement/src/common/movement_dir/mod.rs @@ -1 +1,3 @@ -pub mod movement_dir; \ No newline at end of file +pub mod movement_dir; +pub mod m1; +pub mod manifest; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs b/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs index b1ce4855c..dbfa49b9f 100644 --- a/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs +++ b/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs @@ -1,84 +1,20 @@ use std::path::PathBuf; use serde::{Serialize, Deserialize}; +use super::manifest::MovementDirManifest; #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct M1Manifest { - pub m1_source : Option, - pub subnet_binary : Option, - pub proxy_binary : Option -} - -impl M1Manifest { - pub fn new(m1_source : Option, subnet_binary : Option, proxy_binary : Option) -> Self { - Self { - m1_source, - subnet_binary, - proxy_binary - } - } -} - -impl Default for M1Manifest { - fn default() -> Self { - Self { - m1_source : None, - subnet_binary : None, - proxy_binary : None - } - } -} - - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MovementDirManifest { - pub movement_dir : PathBuf, - pub movement_binary : PathBuf, - pub m1 : Option -} - -#[derive(Debug, Clone)] pub struct MovementDir { - pub path: PathBuf, pub manifest : MovementDirManifest } impl MovementDir { // basic operations - pub fn new(path: PathBuf) -> Self { - Self { path } - } - - pub fn path(&self) -> &PathBuf { - &self.path - } - - pub fn path_str(&self) -> &str { - self.path.to_str().unwrap() - } - - pub fn path_str_with_trailing_slash(&self) -> String { - format!("{}/", self.path_str()) - } - - pub fn path_str_without_trailing_slash(&self) -> String { - self.path_str().to_string() - } - - // m1 assets - pub fn write_m1_source(&self, m1_source : PathBuf) -> Result<(), std::io::Error> { - let mut manifest = self.manifest.clone(); - manifest.m1 = Some(M1Manifest::new(Some(m1_source), None, None)); - self.write_manifest(manifest) - } - -} - -impl Default for MovementDir { - fn default() -> Self { + pub fn new(manifest : MovementDirManifest) -> Self { Self { - // ! default path is $HOME/.movement - path : + manifest } } + + } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_releases/m1.rs b/movement-sdk/clis/movement/src/common/movement_releases/m1.rs new file mode 100644 index 000000000..763966f36 --- /dev/null +++ b/movement-sdk/clis/movement/src/common/movement_releases/m1.rs @@ -0,0 +1,109 @@ +use serde::{Serialize, Deserialize}; +use super::{Release, movement_releases::MovementGitHubRelease}; +use crate::common::util::Version; + +pub static M1_GITHUB_RELEASES : &str = "https://github.com/movemntdev/M1/releases"; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct M1GitHubReleases { + pub m1_source : Release, + pub m1_source_with_submodules : Release, + pub m1_subnet_binary : Release, +} + +impl M1GitHubReleases { + + pub fn from_os_arch(version : &Version) -> Self { + Self { + m1_source : MovementGitHubRelease::new( + "movemntdev".to_string(), + "M1".to_string(), + version.clone(), + "m1-source".to_string() + ).into(), + m1_source_with_submodules : MovementGitHubRelease::new( + "movemntdev".to_string(), + "M1".to_string(), + version.clone(), + "m1-source-with-submodules".to_string() + ).into(), + m1_subnet_binary : MovementGitHubRelease::new( + "movemntdev".to_string(), + "M1".to_string(), + version.clone(), + "subnet".to_string() + ).into() + + } + } + +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum M1Releases { + GitHub(M1GitHubReleases) +} + +impl M1Releases { + + pub fn from_os_arch(version : &Version) -> Self { + Self::GitHub(M1GitHubReleases::from_os_arch(version)) + } + + pub fn m1_source(&self) -> &Release { + match self { + Self::GitHub(releases) => &releases.m1_source + } + } + + pub fn m1_source_with_submodules(&self) -> &Release { + match self { + Self::GitHub(releases) => &releases.m1_source_with_submodules + } + } + + pub fn m1_subnet_binary(&self) -> &Release { + match self { + Self::GitHub(releases) => &releases.m1_subnet_binary + } + } + +} + +#[cfg(test)] +mod test { + + use std::{ + thread::sleep, + time::Duration as duration + }; + + use super::*; + + // this is primarily for a manual check right now + // run this and check the dir which is printed. + #[tokio::test] + async fn test_m1_github_releases() -> Result<(), anyhow::Error> { + + let m1_releases = M1Releases::from_os_arch(&Version::Latest); + println!("{:?}", m1_releases); + + // tmp dir + let tmp_dir = tempfile::tempdir().unwrap(); + println!("tmp_dir: {:?}", tmp_dir); + + // get all of the releases + m1_releases.m1_subnet_binary().to_file(&tmp_dir.path().join("subnet")).await?; + m1_releases.m1_source().to_file(&tmp_dir.path().join("m1-source")).await?; + m1_releases.m1_source_with_submodules().to_file(&tmp_dir.path().join("m1-source-with-submodules")).await?; + + // check that they are there + assert!(tmp_dir.path().join("subnet").exists()); + assert!(tmp_dir.path().join("m1-source").exists()); + assert!(tmp_dir.path().join("m1-source-with-submodules").exists()); + + Ok(()) + + } + +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_releases/mod.rs b/movement-sdk/clis/movement/src/common/movement_releases/mod.rs new file mode 100644 index 000000000..e1a049df6 --- /dev/null +++ b/movement-sdk/clis/movement/src/common/movement_releases/mod.rs @@ -0,0 +1,7 @@ +pub mod movement_releases; +pub use movement_releases::{ + MovementReleases, + Release +}; +pub mod movement_cli; +pub mod m1; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_releases/movement_cli.rs b/movement-sdk/clis/movement/src/common/movement_releases/movement_cli.rs new file mode 100644 index 000000000..4a8d91857 --- /dev/null +++ b/movement-sdk/clis/movement/src/common/movement_releases/movement_cli.rs @@ -0,0 +1,12 @@ +use serde::{Serialize, Deserialize}; +use super::Release; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MovementGitHubReleases { + movement_binary : Release, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum MovementCliReleases { + GitHub(MovementGitHubReleases) +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_releases/movement_releases.rs b/movement-sdk/clis/movement/src/common/movement_releases/movement_releases.rs new file mode 100644 index 000000000..e23f6078b --- /dev/null +++ b/movement-sdk/clis/movement/src/common/movement_releases/movement_releases.rs @@ -0,0 +1,129 @@ +use serde::{Serialize, Deserialize}; +use std::path::{PathBuf, Path}; +use super::m1::M1Releases; +use reqwest; +use std::io::Write; +use crate::common::util::Version; + +#[derive(Debug, Clone, Serialize, Deserialize)] + +pub enum Release { + HttpGET(String), + File(PathBuf), + Unknown +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MovementGitHubRelease { + pub owner : String, + pub repo : String, + pub version : Version, + pub asset : String +} + +impl MovementGitHubRelease { + + pub fn new(owner : String, repo : String, version : Version, asset : String) -> Self { + Self { + owner, + repo, + version, + asset + } + } + + pub fn os_arch_release_url(&self) -> String { + match &self.version { + Version::Latest => { + format!("https://github.com/{}/{}/releases/latest/download/{}-{}-{}", self.owner, self.repo, self.asset, std::env::consts::OS, std::env::consts::ARCH) + }, + Version::Version(version) => { + format!("https://github.com/{}/{}/releases/download/{}/{}-{}-{}", self.owner, self.repo, version, self.asset, std::env::consts::OS, std::env::consts::ARCH) + } + } + } + +} + +impl Into for MovementGitHubRelease { + fn into(self) -> Release { + Release::HttpGET(self.os_arch_release_url()) + } +} + +impl Release { + + pub async fn to_file(&self, path : &Path) -> Result<(), anyhow::Error> { + match self { + Release::HttpGET(url) => { + let mut response = reqwest::get(url).await?; + if response.status().is_success() { + let mut file = std::fs::File::create(path)?; + while let Some(chunk) = response.chunk().await? { + file.write_all(&chunk)?; + } + Ok(()) + } else { + // Handle HTTP errors + Err(anyhow::format_err!("HTTP request failed with status: {}", response.status())) + } + }, + Release::File(to_path) => { + // copy the release file to the path + std::fs::copy(path, to_path)?; + Ok(()) + }, + _ => { + Err(anyhow::format_err!("Cannot get release to file for unknown release type.")) + } + } + } + + +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MovementReleases { + m1_releases : M1Releases +} + +#[cfg(test)] +mod test { + + use super::*; + use tempfile::tempdir; + + #[tokio::test] + async fn test_get_release_to_file() -> Result<(), anyhow::Error> { + + let release_text = "hello"; + + let release = Release::HttpGET(String::from("https://github.com/movemntdev/resources/releases/download/v0.0.0/hello.txt")); + + let dir = tempdir().unwrap(); + let path = dir.path().join("test.txt"); + + release.to_file(&path).await.unwrap(); + + let contents = std::fs::read_to_string(&path).unwrap(); + + assert_eq!(contents, release_text); + + Ok(()) + + } + + #[tokio::test] + async fn test_fails_for_nonexistent_release() -> Result<(), anyhow::Error> { + + let release = Release::HttpGET(String::from("https://github.com/invalid/uri")); + let dir = tempdir().unwrap(); + let path = dir.path().join("test.txt"); + + release.to_file(&path).await.expect_err("Should fail for nonexistent release."); + + Ok(()) + + } + +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/util/file.rs b/movement-sdk/clis/movement/src/common/util/file.rs new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/clis/movement/src/common/util/mod.rs b/movement-sdk/clis/movement/src/common/util/mod.rs new file mode 100644 index 000000000..2cdee5a9f --- /dev/null +++ b/movement-sdk/clis/movement/src/common/util/mod.rs @@ -0,0 +1,4 @@ +pub mod util; +pub mod file; +pub mod version; +pub use version::Version; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/util/util.rs b/movement-sdk/clis/movement/src/common/util/util.rs new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/clis/movement/src/common/util/version.rs b/movement-sdk/clis/movement/src/common/util/version.rs new file mode 100644 index 000000000..7e591d77f --- /dev/null +++ b/movement-sdk/clis/movement/src/common/util/version.rs @@ -0,0 +1,8 @@ +use semver::Version as SemVerVersion; +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum Version { + Latest, + Version(SemVerVersion), +} diff --git a/movement-sdk/clis/movement/src/manage/install/options.rs b/movement-sdk/clis/movement/src/manage/install/options.rs index 3082edea0..198dcf2da 100644 --- a/movement-sdk/clis/movement/src/manage/install/options.rs +++ b/movement-sdk/clis/movement/src/manage/install/options.rs @@ -1,4 +1,4 @@ -#[derive(Debug, StructOpt)] +#[derive(Debug)] pub struct Options { } \ No newline at end of file From 5325bd3502e61ad26c78c824270e9b8389ccf4c0 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 1 Dec 2023 14:16:34 -0800 Subject: [PATCH 36/58] feat: integrated sui and aptos. --- m1/Cargo.toml | 2 +- m1/{movement => m1-cli}/CHANGELOG.md | 0 m1/{movement => m1-cli}/Cargo.toml | 2 +- m1/{movement => m1-cli}/README.md | 0 m1/{movement => m1-cli}/build.rs | 0 m1/{movement => m1-cli}/debug-move-example/Move.toml | 0 .../debug-move-example/sources/DebugDemo.move | 0 m1/{movement => m1-cli}/e2e/README.md | 0 m1/{movement => m1-cli}/e2e/cases/__init__.py | 0 m1/{movement => m1-cli}/e2e/cases/account.py | 0 m1/{movement => m1-cli}/e2e/cases/init.py | 0 m1/{movement => m1-cli}/e2e/common.py | 0 m1/{movement => m1-cli}/e2e/local_testnet.py | 0 m1/{movement => m1-cli}/e2e/main.py | 0 m1/{movement => m1-cli}/e2e/poetry.lock | 0 m1/{movement => m1-cli}/e2e/pyproject.toml | 0 m1/{movement => m1-cli}/e2e/test_helpers.py | 0 m1/{movement => m1-cli}/e2e/test_results.py | 0 m1/{movement => m1-cli}/homebrew/README.md | 0 m1/{movement => m1-cli}/homebrew/aptos.rb | 0 m1/{movement => m1-cli}/src/account/create.rs | 0 m1/{movement => m1-cli}/src/account/create_resource_account.rs | 0 m1/{movement => m1-cli}/src/account/derive_resource_account.rs | 0 m1/{movement => m1-cli}/src/account/fund.rs | 0 m1/{movement => m1-cli}/src/account/key_rotation.rs | 0 m1/{movement => m1-cli}/src/account/list.rs | 0 m1/{movement => m1-cli}/src/account/mod.rs | 0 m1/{movement => m1-cli}/src/account/multisig_account.rs | 0 m1/{movement => m1-cli}/src/account/transfer.rs | 0 m1/{movement => m1-cli}/src/common/init.rs | 0 m1/{movement => m1-cli}/src/common/mod.rs | 0 m1/{movement => m1-cli}/src/common/types.rs | 0 m1/{movement => m1-cli}/src/common/utils.rs | 0 m1/{movement => m1-cli}/src/config/mod.rs | 0 m1/{movement => m1-cli}/src/faucet/mod.rs | 0 m1/{movement => m1-cli}/src/ffi.rs | 0 m1/{movement => m1-cli}/src/genesis/git.rs | 0 m1/{movement => m1-cli}/src/genesis/keys.rs | 0 m1/{movement => m1-cli}/src/genesis/mod.rs | 0 m1/{movement => m1-cli}/src/genesis/tests.rs | 0 m1/{movement => m1-cli}/src/genesis/tools.rs | 0 m1/{movement => m1-cli}/src/governance/mod.rs | 0 m1/{movement => m1-cli}/src/lib.rs | 2 +- m1/{movement => m1-cli}/src/main.rs | 2 +- m1/{movement => m1-cli}/src/move_tool/aptos_debug_natives.rs | 0 .../src/move_tool/aptos_dep_example/README.md | 0 .../src/move_tool/aptos_dep_example/pack1/Move.toml | 0 .../src/move_tool/aptos_dep_example/pack1/sources/hello.move | 0 .../src/move_tool/aptos_dep_example/pack2/Move.toml | 0 .../src/move_tool/aptos_dep_example/pack2/sources/m.move | 0 m1/{movement => m1-cli}/src/move_tool/coverage.rs | 0 m1/{movement => m1-cli}/src/move_tool/disassembler.rs | 0 m1/{movement => m1-cli}/src/move_tool/manifest.rs | 0 m1/{movement => m1-cli}/src/move_tool/mod.rs | 0 m1/{movement => m1-cli}/src/move_tool/package_hooks.rs | 0 m1/{movement => m1-cli}/src/move_tool/show.rs | 0 m1/{movement => m1-cli}/src/move_tool/stored_package.rs | 0 .../src/move_tool/transactional_tests_runner.rs | 0 m1/{movement => m1-cli}/src/node/analyze/analyze_validators.rs | 0 m1/{movement => m1-cli}/src/node/analyze/fetch_metadata.rs | 0 m1/{movement => m1-cli}/src/node/analyze/mod.rs | 0 m1/{movement => m1-cli}/src/node/mod.rs | 0 m1/{movement => m1-cli}/src/op/key.rs | 0 m1/{movement => m1-cli}/src/op/mod.rs | 0 m1/{movement => m1-cli}/src/stake/mod.rs | 0 m1/{movement => m1-cli}/src/test/mod.rs | 0 m1/{movement => m1-cli}/src/test/tests.rs | 0 m1/{movement => m1-cli}/src/update/helpers.rs | 0 m1/{movement => m1-cli}/src/update/mod.rs | 0 m1/{movement => m1-cli}/src/update/tool.rs | 0 70 files changed, 4 insertions(+), 4 deletions(-) rename m1/{movement => m1-cli}/CHANGELOG.md (100%) rename m1/{movement => m1-cli}/Cargo.toml (99%) rename m1/{movement => m1-cli}/README.md (100%) rename m1/{movement => m1-cli}/build.rs (100%) rename m1/{movement => m1-cli}/debug-move-example/Move.toml (100%) rename m1/{movement => m1-cli}/debug-move-example/sources/DebugDemo.move (100%) rename m1/{movement => m1-cli}/e2e/README.md (100%) rename m1/{movement => m1-cli}/e2e/cases/__init__.py (100%) rename m1/{movement => m1-cli}/e2e/cases/account.py (100%) rename m1/{movement => m1-cli}/e2e/cases/init.py (100%) rename m1/{movement => m1-cli}/e2e/common.py (100%) rename m1/{movement => m1-cli}/e2e/local_testnet.py (100%) rename m1/{movement => m1-cli}/e2e/main.py (100%) rename m1/{movement => m1-cli}/e2e/poetry.lock (100%) rename m1/{movement => m1-cli}/e2e/pyproject.toml (100%) rename m1/{movement => m1-cli}/e2e/test_helpers.py (100%) rename m1/{movement => m1-cli}/e2e/test_results.py (100%) rename m1/{movement => m1-cli}/homebrew/README.md (100%) rename m1/{movement => m1-cli}/homebrew/aptos.rb (100%) rename m1/{movement => m1-cli}/src/account/create.rs (100%) rename m1/{movement => m1-cli}/src/account/create_resource_account.rs (100%) rename m1/{movement => m1-cli}/src/account/derive_resource_account.rs (100%) rename m1/{movement => m1-cli}/src/account/fund.rs (100%) rename m1/{movement => m1-cli}/src/account/key_rotation.rs (100%) rename m1/{movement => m1-cli}/src/account/list.rs (100%) rename m1/{movement => m1-cli}/src/account/mod.rs (100%) rename m1/{movement => m1-cli}/src/account/multisig_account.rs (100%) rename m1/{movement => m1-cli}/src/account/transfer.rs (100%) rename m1/{movement => m1-cli}/src/common/init.rs (100%) rename m1/{movement => m1-cli}/src/common/mod.rs (100%) rename m1/{movement => m1-cli}/src/common/types.rs (100%) rename m1/{movement => m1-cli}/src/common/utils.rs (100%) rename m1/{movement => m1-cli}/src/config/mod.rs (100%) rename m1/{movement => m1-cli}/src/faucet/mod.rs (100%) rename m1/{movement => m1-cli}/src/ffi.rs (100%) rename m1/{movement => m1-cli}/src/genesis/git.rs (100%) rename m1/{movement => m1-cli}/src/genesis/keys.rs (100%) rename m1/{movement => m1-cli}/src/genesis/mod.rs (100%) rename m1/{movement => m1-cli}/src/genesis/tests.rs (100%) rename m1/{movement => m1-cli}/src/genesis/tools.rs (100%) rename m1/{movement => m1-cli}/src/governance/mod.rs (100%) rename m1/{movement => m1-cli}/src/lib.rs (97%) rename m1/{movement => m1-cli}/src/main.rs (95%) rename m1/{movement => m1-cli}/src/move_tool/aptos_debug_natives.rs (100%) rename m1/{movement => m1-cli}/src/move_tool/aptos_dep_example/README.md (100%) rename m1/{movement => m1-cli}/src/move_tool/aptos_dep_example/pack1/Move.toml (100%) rename m1/{movement => m1-cli}/src/move_tool/aptos_dep_example/pack1/sources/hello.move (100%) rename m1/{movement => m1-cli}/src/move_tool/aptos_dep_example/pack2/Move.toml (100%) rename m1/{movement => m1-cli}/src/move_tool/aptos_dep_example/pack2/sources/m.move (100%) rename m1/{movement => m1-cli}/src/move_tool/coverage.rs (100%) rename m1/{movement => m1-cli}/src/move_tool/disassembler.rs (100%) rename m1/{movement => m1-cli}/src/move_tool/manifest.rs (100%) rename m1/{movement => m1-cli}/src/move_tool/mod.rs (100%) rename m1/{movement => m1-cli}/src/move_tool/package_hooks.rs (100%) rename m1/{movement => m1-cli}/src/move_tool/show.rs (100%) rename m1/{movement => m1-cli}/src/move_tool/stored_package.rs (100%) rename m1/{movement => m1-cli}/src/move_tool/transactional_tests_runner.rs (100%) rename m1/{movement => m1-cli}/src/node/analyze/analyze_validators.rs (100%) rename m1/{movement => m1-cli}/src/node/analyze/fetch_metadata.rs (100%) rename m1/{movement => m1-cli}/src/node/analyze/mod.rs (100%) rename m1/{movement => m1-cli}/src/node/mod.rs (100%) rename m1/{movement => m1-cli}/src/op/key.rs (100%) rename m1/{movement => m1-cli}/src/op/mod.rs (100%) rename m1/{movement => m1-cli}/src/stake/mod.rs (100%) rename m1/{movement => m1-cli}/src/test/mod.rs (100%) rename m1/{movement => m1-cli}/src/test/tests.rs (100%) rename m1/{movement => m1-cli}/src/update/helpers.rs (100%) rename m1/{movement => m1-cli}/src/update/mod.rs (100%) rename m1/{movement => m1-cli}/src/update/tool.rs (100%) diff --git a/m1/Cargo.toml b/m1/Cargo.toml index ae2db931b..90f3efeda 100644 --- a/m1/Cargo.toml +++ b/m1/Cargo.toml @@ -2,7 +2,7 @@ resolver = "2" members = [ "subnet", - "movement", + "m1-cli", "movement-benchmark", "tests/e2e" ] diff --git a/m1/movement/CHANGELOG.md b/m1/m1-cli/CHANGELOG.md similarity index 100% rename from m1/movement/CHANGELOG.md rename to m1/m1-cli/CHANGELOG.md diff --git a/m1/movement/Cargo.toml b/m1/m1-cli/Cargo.toml similarity index 99% rename from m1/movement/Cargo.toml rename to m1/m1-cli/Cargo.toml index 3e4879b02..ca6b89c9c 100644 --- a/m1/movement/Cargo.toml +++ b/m1/m1-cli/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "movement" +name = "m1-cli" description = "Movement tool for management of nodes and interacting with the blockchain. Based on the Movement CLI." version = "1.0.13" diff --git a/m1/movement/README.md b/m1/m1-cli/README.md similarity index 100% rename from m1/movement/README.md rename to m1/m1-cli/README.md diff --git a/m1/movement/build.rs b/m1/m1-cli/build.rs similarity index 100% rename from m1/movement/build.rs rename to m1/m1-cli/build.rs diff --git a/m1/movement/debug-move-example/Move.toml b/m1/m1-cli/debug-move-example/Move.toml similarity index 100% rename from m1/movement/debug-move-example/Move.toml rename to m1/m1-cli/debug-move-example/Move.toml diff --git a/m1/movement/debug-move-example/sources/DebugDemo.move b/m1/m1-cli/debug-move-example/sources/DebugDemo.move similarity index 100% rename from m1/movement/debug-move-example/sources/DebugDemo.move rename to m1/m1-cli/debug-move-example/sources/DebugDemo.move diff --git a/m1/movement/e2e/README.md b/m1/m1-cli/e2e/README.md similarity index 100% rename from m1/movement/e2e/README.md rename to m1/m1-cli/e2e/README.md diff --git a/m1/movement/e2e/cases/__init__.py b/m1/m1-cli/e2e/cases/__init__.py similarity index 100% rename from m1/movement/e2e/cases/__init__.py rename to m1/m1-cli/e2e/cases/__init__.py diff --git a/m1/movement/e2e/cases/account.py b/m1/m1-cli/e2e/cases/account.py similarity index 100% rename from m1/movement/e2e/cases/account.py rename to m1/m1-cli/e2e/cases/account.py diff --git a/m1/movement/e2e/cases/init.py b/m1/m1-cli/e2e/cases/init.py similarity index 100% rename from m1/movement/e2e/cases/init.py rename to m1/m1-cli/e2e/cases/init.py diff --git a/m1/movement/e2e/common.py b/m1/m1-cli/e2e/common.py similarity index 100% rename from m1/movement/e2e/common.py rename to m1/m1-cli/e2e/common.py diff --git a/m1/movement/e2e/local_testnet.py b/m1/m1-cli/e2e/local_testnet.py similarity index 100% rename from m1/movement/e2e/local_testnet.py rename to m1/m1-cli/e2e/local_testnet.py diff --git a/m1/movement/e2e/main.py b/m1/m1-cli/e2e/main.py similarity index 100% rename from m1/movement/e2e/main.py rename to m1/m1-cli/e2e/main.py diff --git a/m1/movement/e2e/poetry.lock b/m1/m1-cli/e2e/poetry.lock similarity index 100% rename from m1/movement/e2e/poetry.lock rename to m1/m1-cli/e2e/poetry.lock diff --git a/m1/movement/e2e/pyproject.toml b/m1/m1-cli/e2e/pyproject.toml similarity index 100% rename from m1/movement/e2e/pyproject.toml rename to m1/m1-cli/e2e/pyproject.toml diff --git a/m1/movement/e2e/test_helpers.py b/m1/m1-cli/e2e/test_helpers.py similarity index 100% rename from m1/movement/e2e/test_helpers.py rename to m1/m1-cli/e2e/test_helpers.py diff --git a/m1/movement/e2e/test_results.py b/m1/m1-cli/e2e/test_results.py similarity index 100% rename from m1/movement/e2e/test_results.py rename to m1/m1-cli/e2e/test_results.py diff --git a/m1/movement/homebrew/README.md b/m1/m1-cli/homebrew/README.md similarity index 100% rename from m1/movement/homebrew/README.md rename to m1/m1-cli/homebrew/README.md diff --git a/m1/movement/homebrew/aptos.rb b/m1/m1-cli/homebrew/aptos.rb similarity index 100% rename from m1/movement/homebrew/aptos.rb rename to m1/m1-cli/homebrew/aptos.rb diff --git a/m1/movement/src/account/create.rs b/m1/m1-cli/src/account/create.rs similarity index 100% rename from m1/movement/src/account/create.rs rename to m1/m1-cli/src/account/create.rs diff --git a/m1/movement/src/account/create_resource_account.rs b/m1/m1-cli/src/account/create_resource_account.rs similarity index 100% rename from m1/movement/src/account/create_resource_account.rs rename to m1/m1-cli/src/account/create_resource_account.rs diff --git a/m1/movement/src/account/derive_resource_account.rs b/m1/m1-cli/src/account/derive_resource_account.rs similarity index 100% rename from m1/movement/src/account/derive_resource_account.rs rename to m1/m1-cli/src/account/derive_resource_account.rs diff --git a/m1/movement/src/account/fund.rs b/m1/m1-cli/src/account/fund.rs similarity index 100% rename from m1/movement/src/account/fund.rs rename to m1/m1-cli/src/account/fund.rs diff --git a/m1/movement/src/account/key_rotation.rs b/m1/m1-cli/src/account/key_rotation.rs similarity index 100% rename from m1/movement/src/account/key_rotation.rs rename to m1/m1-cli/src/account/key_rotation.rs diff --git a/m1/movement/src/account/list.rs b/m1/m1-cli/src/account/list.rs similarity index 100% rename from m1/movement/src/account/list.rs rename to m1/m1-cli/src/account/list.rs diff --git a/m1/movement/src/account/mod.rs b/m1/m1-cli/src/account/mod.rs similarity index 100% rename from m1/movement/src/account/mod.rs rename to m1/m1-cli/src/account/mod.rs diff --git a/m1/movement/src/account/multisig_account.rs b/m1/m1-cli/src/account/multisig_account.rs similarity index 100% rename from m1/movement/src/account/multisig_account.rs rename to m1/m1-cli/src/account/multisig_account.rs diff --git a/m1/movement/src/account/transfer.rs b/m1/m1-cli/src/account/transfer.rs similarity index 100% rename from m1/movement/src/account/transfer.rs rename to m1/m1-cli/src/account/transfer.rs diff --git a/m1/movement/src/common/init.rs b/m1/m1-cli/src/common/init.rs similarity index 100% rename from m1/movement/src/common/init.rs rename to m1/m1-cli/src/common/init.rs diff --git a/m1/movement/src/common/mod.rs b/m1/m1-cli/src/common/mod.rs similarity index 100% rename from m1/movement/src/common/mod.rs rename to m1/m1-cli/src/common/mod.rs diff --git a/m1/movement/src/common/types.rs b/m1/m1-cli/src/common/types.rs similarity index 100% rename from m1/movement/src/common/types.rs rename to m1/m1-cli/src/common/types.rs diff --git a/m1/movement/src/common/utils.rs b/m1/m1-cli/src/common/utils.rs similarity index 100% rename from m1/movement/src/common/utils.rs rename to m1/m1-cli/src/common/utils.rs diff --git a/m1/movement/src/config/mod.rs b/m1/m1-cli/src/config/mod.rs similarity index 100% rename from m1/movement/src/config/mod.rs rename to m1/m1-cli/src/config/mod.rs diff --git a/m1/movement/src/faucet/mod.rs b/m1/m1-cli/src/faucet/mod.rs similarity index 100% rename from m1/movement/src/faucet/mod.rs rename to m1/m1-cli/src/faucet/mod.rs diff --git a/m1/movement/src/ffi.rs b/m1/m1-cli/src/ffi.rs similarity index 100% rename from m1/movement/src/ffi.rs rename to m1/m1-cli/src/ffi.rs diff --git a/m1/movement/src/genesis/git.rs b/m1/m1-cli/src/genesis/git.rs similarity index 100% rename from m1/movement/src/genesis/git.rs rename to m1/m1-cli/src/genesis/git.rs diff --git a/m1/movement/src/genesis/keys.rs b/m1/m1-cli/src/genesis/keys.rs similarity index 100% rename from m1/movement/src/genesis/keys.rs rename to m1/m1-cli/src/genesis/keys.rs diff --git a/m1/movement/src/genesis/mod.rs b/m1/m1-cli/src/genesis/mod.rs similarity index 100% rename from m1/movement/src/genesis/mod.rs rename to m1/m1-cli/src/genesis/mod.rs diff --git a/m1/movement/src/genesis/tests.rs b/m1/m1-cli/src/genesis/tests.rs similarity index 100% rename from m1/movement/src/genesis/tests.rs rename to m1/m1-cli/src/genesis/tests.rs diff --git a/m1/movement/src/genesis/tools.rs b/m1/m1-cli/src/genesis/tools.rs similarity index 100% rename from m1/movement/src/genesis/tools.rs rename to m1/m1-cli/src/genesis/tools.rs diff --git a/m1/movement/src/governance/mod.rs b/m1/m1-cli/src/governance/mod.rs similarity index 100% rename from m1/movement/src/governance/mod.rs rename to m1/m1-cli/src/governance/mod.rs diff --git a/m1/movement/src/lib.rs b/m1/m1-cli/src/lib.rs similarity index 97% rename from m1/movement/src/lib.rs rename to m1/m1-cli/src/lib.rs index ee7e8a28c..b8475e281 100644 --- a/m1/movement/src/lib.rs +++ b/m1/m1-cli/src/lib.rs @@ -28,7 +28,7 @@ use std::collections::BTreeMap; /// Command Line Interface (CLI) for developing and interacting with the Aptos blockchain #[derive(Parser)] -#[clap(name = "movement", author, version, propagate_version = true)] +#[clap(name = "aptos", author, version, propagate_version = true)] pub enum Tool { #[clap(subcommand)] Account(account::AccountTool), diff --git a/m1/movement/src/main.rs b/m1/m1-cli/src/main.rs similarity index 95% rename from m1/movement/src/main.rs rename to m1/m1-cli/src/main.rs index faf840e2d..4fff4f71d 100644 --- a/m1/movement/src/main.rs +++ b/m1/m1-cli/src/main.rs @@ -9,7 +9,7 @@ #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; -use movement::{move_tool, Tool}; +pub use movement::{move_tool, Tool}; use clap::Parser; use std::process::exit; diff --git a/m1/movement/src/move_tool/aptos_debug_natives.rs b/m1/m1-cli/src/move_tool/aptos_debug_natives.rs similarity index 100% rename from m1/movement/src/move_tool/aptos_debug_natives.rs rename to m1/m1-cli/src/move_tool/aptos_debug_natives.rs diff --git a/m1/movement/src/move_tool/aptos_dep_example/README.md b/m1/m1-cli/src/move_tool/aptos_dep_example/README.md similarity index 100% rename from m1/movement/src/move_tool/aptos_dep_example/README.md rename to m1/m1-cli/src/move_tool/aptos_dep_example/README.md diff --git a/m1/movement/src/move_tool/aptos_dep_example/pack1/Move.toml b/m1/m1-cli/src/move_tool/aptos_dep_example/pack1/Move.toml similarity index 100% rename from m1/movement/src/move_tool/aptos_dep_example/pack1/Move.toml rename to m1/m1-cli/src/move_tool/aptos_dep_example/pack1/Move.toml diff --git a/m1/movement/src/move_tool/aptos_dep_example/pack1/sources/hello.move b/m1/m1-cli/src/move_tool/aptos_dep_example/pack1/sources/hello.move similarity index 100% rename from m1/movement/src/move_tool/aptos_dep_example/pack1/sources/hello.move rename to m1/m1-cli/src/move_tool/aptos_dep_example/pack1/sources/hello.move diff --git a/m1/movement/src/move_tool/aptos_dep_example/pack2/Move.toml b/m1/m1-cli/src/move_tool/aptos_dep_example/pack2/Move.toml similarity index 100% rename from m1/movement/src/move_tool/aptos_dep_example/pack2/Move.toml rename to m1/m1-cli/src/move_tool/aptos_dep_example/pack2/Move.toml diff --git a/m1/movement/src/move_tool/aptos_dep_example/pack2/sources/m.move b/m1/m1-cli/src/move_tool/aptos_dep_example/pack2/sources/m.move similarity index 100% rename from m1/movement/src/move_tool/aptos_dep_example/pack2/sources/m.move rename to m1/m1-cli/src/move_tool/aptos_dep_example/pack2/sources/m.move diff --git a/m1/movement/src/move_tool/coverage.rs b/m1/m1-cli/src/move_tool/coverage.rs similarity index 100% rename from m1/movement/src/move_tool/coverage.rs rename to m1/m1-cli/src/move_tool/coverage.rs diff --git a/m1/movement/src/move_tool/disassembler.rs b/m1/m1-cli/src/move_tool/disassembler.rs similarity index 100% rename from m1/movement/src/move_tool/disassembler.rs rename to m1/m1-cli/src/move_tool/disassembler.rs diff --git a/m1/movement/src/move_tool/manifest.rs b/m1/m1-cli/src/move_tool/manifest.rs similarity index 100% rename from m1/movement/src/move_tool/manifest.rs rename to m1/m1-cli/src/move_tool/manifest.rs diff --git a/m1/movement/src/move_tool/mod.rs b/m1/m1-cli/src/move_tool/mod.rs similarity index 100% rename from m1/movement/src/move_tool/mod.rs rename to m1/m1-cli/src/move_tool/mod.rs diff --git a/m1/movement/src/move_tool/package_hooks.rs b/m1/m1-cli/src/move_tool/package_hooks.rs similarity index 100% rename from m1/movement/src/move_tool/package_hooks.rs rename to m1/m1-cli/src/move_tool/package_hooks.rs diff --git a/m1/movement/src/move_tool/show.rs b/m1/m1-cli/src/move_tool/show.rs similarity index 100% rename from m1/movement/src/move_tool/show.rs rename to m1/m1-cli/src/move_tool/show.rs diff --git a/m1/movement/src/move_tool/stored_package.rs b/m1/m1-cli/src/move_tool/stored_package.rs similarity index 100% rename from m1/movement/src/move_tool/stored_package.rs rename to m1/m1-cli/src/move_tool/stored_package.rs diff --git a/m1/movement/src/move_tool/transactional_tests_runner.rs b/m1/m1-cli/src/move_tool/transactional_tests_runner.rs similarity index 100% rename from m1/movement/src/move_tool/transactional_tests_runner.rs rename to m1/m1-cli/src/move_tool/transactional_tests_runner.rs diff --git a/m1/movement/src/node/analyze/analyze_validators.rs b/m1/m1-cli/src/node/analyze/analyze_validators.rs similarity index 100% rename from m1/movement/src/node/analyze/analyze_validators.rs rename to m1/m1-cli/src/node/analyze/analyze_validators.rs diff --git a/m1/movement/src/node/analyze/fetch_metadata.rs b/m1/m1-cli/src/node/analyze/fetch_metadata.rs similarity index 100% rename from m1/movement/src/node/analyze/fetch_metadata.rs rename to m1/m1-cli/src/node/analyze/fetch_metadata.rs diff --git a/m1/movement/src/node/analyze/mod.rs b/m1/m1-cli/src/node/analyze/mod.rs similarity index 100% rename from m1/movement/src/node/analyze/mod.rs rename to m1/m1-cli/src/node/analyze/mod.rs diff --git a/m1/movement/src/node/mod.rs b/m1/m1-cli/src/node/mod.rs similarity index 100% rename from m1/movement/src/node/mod.rs rename to m1/m1-cli/src/node/mod.rs diff --git a/m1/movement/src/op/key.rs b/m1/m1-cli/src/op/key.rs similarity index 100% rename from m1/movement/src/op/key.rs rename to m1/m1-cli/src/op/key.rs diff --git a/m1/movement/src/op/mod.rs b/m1/m1-cli/src/op/mod.rs similarity index 100% rename from m1/movement/src/op/mod.rs rename to m1/m1-cli/src/op/mod.rs diff --git a/m1/movement/src/stake/mod.rs b/m1/m1-cli/src/stake/mod.rs similarity index 100% rename from m1/movement/src/stake/mod.rs rename to m1/m1-cli/src/stake/mod.rs diff --git a/m1/movement/src/test/mod.rs b/m1/m1-cli/src/test/mod.rs similarity index 100% rename from m1/movement/src/test/mod.rs rename to m1/m1-cli/src/test/mod.rs diff --git a/m1/movement/src/test/tests.rs b/m1/m1-cli/src/test/tests.rs similarity index 100% rename from m1/movement/src/test/tests.rs rename to m1/m1-cli/src/test/tests.rs diff --git a/m1/movement/src/update/helpers.rs b/m1/m1-cli/src/update/helpers.rs similarity index 100% rename from m1/movement/src/update/helpers.rs rename to m1/m1-cli/src/update/helpers.rs diff --git a/m1/movement/src/update/mod.rs b/m1/m1-cli/src/update/mod.rs similarity index 100% rename from m1/movement/src/update/mod.rs rename to m1/m1-cli/src/update/mod.rs diff --git a/m1/movement/src/update/tool.rs b/m1/m1-cli/src/update/tool.rs similarity index 100% rename from m1/movement/src/update/tool.rs rename to m1/m1-cli/src/update/tool.rs From 18bcab23abd2fb187f768b9285d60227c29056f4 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 1 Dec 2023 21:20:54 -0800 Subject: [PATCH 37/58] feat: integrated sui and aptos. --- canonical/Cargo.lock | 46 +- movement-sdk/Cargo.lock | 16467 ++++++++++++++-- movement-sdk/Cargo.toml | 15 +- movement-sdk/clis/aptos/CHANGELOG.md | 51 + movement-sdk/clis/aptos/Cargo.toml | 119 + movement-sdk/clis/aptos/README.md | 5 + movement-sdk/clis/aptos/build.rs | 6 + .../clis/aptos/debug-move-example/Move.toml | 9 + .../debug-move-example/sources/DebugDemo.move | 32 + movement-sdk/clis/aptos/e2e/README.md | 58 + movement-sdk/clis/aptos/e2e/cases/__init__.py | 0 movement-sdk/clis/aptos/e2e/cases/account.py | 62 + movement-sdk/clis/aptos/e2e/cases/init.py | 43 + movement-sdk/clis/aptos/e2e/common.py | 49 + movement-sdk/clis/aptos/e2e/local_testnet.py | 100 + movement-sdk/clis/aptos/e2e/main.py | 161 + movement-sdk/clis/aptos/e2e/poetry.lock | 665 + movement-sdk/clis/aptos/e2e/pyproject.toml | 19 + movement-sdk/clis/aptos/e2e/test_helpers.py | 174 + movement-sdk/clis/aptos/e2e/test_results.py | 48 + movement-sdk/clis/aptos/homebrew/README.md | 210 + movement-sdk/clis/aptos/homebrew/aptos.rb | 35 + movement-sdk/clis/aptos/src/account/create.rs | 41 + .../src/account/create_resource_account.rs | 91 + .../src/account/derive_resource_account.rs | 114 + movement-sdk/clis/aptos/src/account/fund.rs | 69 + .../clis/aptos/src/account/key_rotation.rs | 338 + movement-sdk/clis/aptos/src/account/list.rs | 124 + movement-sdk/clis/aptos/src/account/mod.rs | 69 + .../aptos/src/account/multisig_account.rs | 257 + .../clis/aptos/src/account/transfer.rs | 115 + movement-sdk/clis/aptos/src/common/init.rs | 374 + movement-sdk/clis/aptos/src/common/mod.rs | 6 + movement-sdk/clis/aptos/src/common/types.rs | 1753 ++ movement-sdk/clis/aptos/src/common/utils.rs | 507 + movement-sdk/clis/aptos/src/config/mod.rs | 360 + movement-sdk/clis/aptos/src/faucet/mod.rs | 52 + movement-sdk/clis/aptos/src/ffi.rs | 85 + movement-sdk/clis/aptos/src/genesis/git.rs | 264 + movement-sdk/clis/aptos/src/genesis/keys.rs | 344 + movement-sdk/clis/aptos/src/genesis/mod.rs | 926 + movement-sdk/clis/aptos/src/genesis/tests.rs | 434 + movement-sdk/clis/aptos/src/genesis/tools.rs | 100 + movement-sdk/clis/aptos/src/governance/mod.rs | 1061 + movement-sdk/clis/aptos/src/lib.rs | 94 + movement-sdk/clis/aptos/src/main.rs | 31 + .../src/move_tool/aptos_debug_natives.rs | 26 + .../src/move_tool/aptos_dep_example/README.md | 24 + .../aptos_dep_example/pack1/Move.toml | 6 + .../pack1/sources/hello.move | 7 + .../aptos_dep_example/pack2/Move.toml | 3 + .../aptos_dep_example/pack2/sources/m.move | 3 + .../clis/aptos/src/move_tool/coverage.rs | 184 + .../clis/aptos/src/move_tool/disassembler.rs | 148 + .../clis/aptos/src/move_tool/manifest.rs | 90 + movement-sdk/clis/aptos/src/move_tool/mod.rs | 1606 ++ .../clis/aptos/src/move_tool/package_hooks.rs | 54 + movement-sdk/clis/aptos/src/move_tool/show.rs | 109 + .../aptos/src/move_tool/stored_package.rs | 214 + .../move_tool/transactional_tests_runner.rs | 345 + .../src/node/analyze/analyze_validators.rs | 540 + .../aptos/src/node/analyze/fetch_metadata.rs | 337 + .../clis/aptos/src/node/analyze/mod.rs | 5 + movement-sdk/clis/aptos/src/node/mod.rs | 1730 ++ movement-sdk/clis/aptos/src/op/key.rs | 396 + movement-sdk/clis/aptos/src/op/mod.rs | 4 + movement-sdk/clis/aptos/src/stake/mod.rs | 668 + movement-sdk/clis/aptos/src/test/mod.rs | 1221 ++ movement-sdk/clis/aptos/src/test/tests.rs | 136 + movement-sdk/clis/aptos/src/update/helpers.rs | 77 + movement-sdk/clis/aptos/src/update/mod.rs | 8 + movement-sdk/clis/aptos/src/update/tool.rs | 145 + movement-sdk/clis/movement/Cargo.toml | 10 +- movement-sdk/clis/movement/src/aptos/aptos.rs | 17 + movement-sdk/clis/movement/src/aptos/mod.rs | 2 + .../clis/movement/src/common/cli/command.rs | 2 +- .../movement/src/common/movement_dir/m1.rs | 57 +- .../src/common/movement_dir/manifest.rs | 132 +- .../movement/src/common/movement_dir/mod.rs | 1 + .../src/common/movement_dir/movement_dir.rs | 17 + .../src/common/movement_releases/m1.rs | 32 +- .../common/movement_releases/movement_cli.rs | 70 +- .../movement_releases/movement_releases.rs | 12 +- movement-sdk/clis/movement/src/ctl/ctl.rs | 4 + .../clis/movement/src/ctl/start/start.rs | 4 + .../clis/movement/src/ctl/status/status.rs | 4 + .../clis/movement/src/ctl/stop/stop.rs | 4 + movement-sdk/clis/movement/src/lib.rs | 79 +- movement-sdk/clis/movement/src/main.rs | 18 +- .../movement/src/manage/install/install.rs | 4 + .../clis/movement/src/manage/manage.rs | 8 +- movement-sdk/clis/movement/src/sui/mod.rs | 2 + movement-sdk/clis/movement/src/sui/sui.rs | 54 + movement-sdk/rust-toolchain | 1 + vendors/aptos-core | 2 +- 95 files changed, 32493 insertions(+), 2112 deletions(-) create mode 100644 movement-sdk/clis/aptos/CHANGELOG.md create mode 100644 movement-sdk/clis/aptos/Cargo.toml create mode 100644 movement-sdk/clis/aptos/README.md create mode 100644 movement-sdk/clis/aptos/build.rs create mode 100644 movement-sdk/clis/aptos/debug-move-example/Move.toml create mode 100644 movement-sdk/clis/aptos/debug-move-example/sources/DebugDemo.move create mode 100644 movement-sdk/clis/aptos/e2e/README.md create mode 100644 movement-sdk/clis/aptos/e2e/cases/__init__.py create mode 100644 movement-sdk/clis/aptos/e2e/cases/account.py create mode 100644 movement-sdk/clis/aptos/e2e/cases/init.py create mode 100644 movement-sdk/clis/aptos/e2e/common.py create mode 100644 movement-sdk/clis/aptos/e2e/local_testnet.py create mode 100644 movement-sdk/clis/aptos/e2e/main.py create mode 100644 movement-sdk/clis/aptos/e2e/poetry.lock create mode 100644 movement-sdk/clis/aptos/e2e/pyproject.toml create mode 100644 movement-sdk/clis/aptos/e2e/test_helpers.py create mode 100644 movement-sdk/clis/aptos/e2e/test_results.py create mode 100644 movement-sdk/clis/aptos/homebrew/README.md create mode 100644 movement-sdk/clis/aptos/homebrew/aptos.rb create mode 100644 movement-sdk/clis/aptos/src/account/create.rs create mode 100644 movement-sdk/clis/aptos/src/account/create_resource_account.rs create mode 100644 movement-sdk/clis/aptos/src/account/derive_resource_account.rs create mode 100644 movement-sdk/clis/aptos/src/account/fund.rs create mode 100644 movement-sdk/clis/aptos/src/account/key_rotation.rs create mode 100644 movement-sdk/clis/aptos/src/account/list.rs create mode 100644 movement-sdk/clis/aptos/src/account/mod.rs create mode 100644 movement-sdk/clis/aptos/src/account/multisig_account.rs create mode 100644 movement-sdk/clis/aptos/src/account/transfer.rs create mode 100644 movement-sdk/clis/aptos/src/common/init.rs create mode 100644 movement-sdk/clis/aptos/src/common/mod.rs create mode 100644 movement-sdk/clis/aptos/src/common/types.rs create mode 100644 movement-sdk/clis/aptos/src/common/utils.rs create mode 100644 movement-sdk/clis/aptos/src/config/mod.rs create mode 100644 movement-sdk/clis/aptos/src/faucet/mod.rs create mode 100644 movement-sdk/clis/aptos/src/ffi.rs create mode 100644 movement-sdk/clis/aptos/src/genesis/git.rs create mode 100644 movement-sdk/clis/aptos/src/genesis/keys.rs create mode 100644 movement-sdk/clis/aptos/src/genesis/mod.rs create mode 100644 movement-sdk/clis/aptos/src/genesis/tests.rs create mode 100644 movement-sdk/clis/aptos/src/genesis/tools.rs create mode 100644 movement-sdk/clis/aptos/src/governance/mod.rs create mode 100644 movement-sdk/clis/aptos/src/lib.rs create mode 100644 movement-sdk/clis/aptos/src/main.rs create mode 100644 movement-sdk/clis/aptos/src/move_tool/aptos_debug_natives.rs create mode 100644 movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/README.md create mode 100644 movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/pack1/Move.toml create mode 100644 movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/pack1/sources/hello.move create mode 100644 movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/pack2/Move.toml create mode 100644 movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/pack2/sources/m.move create mode 100644 movement-sdk/clis/aptos/src/move_tool/coverage.rs create mode 100644 movement-sdk/clis/aptos/src/move_tool/disassembler.rs create mode 100644 movement-sdk/clis/aptos/src/move_tool/manifest.rs create mode 100644 movement-sdk/clis/aptos/src/move_tool/mod.rs create mode 100644 movement-sdk/clis/aptos/src/move_tool/package_hooks.rs create mode 100644 movement-sdk/clis/aptos/src/move_tool/show.rs create mode 100644 movement-sdk/clis/aptos/src/move_tool/stored_package.rs create mode 100644 movement-sdk/clis/aptos/src/move_tool/transactional_tests_runner.rs create mode 100644 movement-sdk/clis/aptos/src/node/analyze/analyze_validators.rs create mode 100644 movement-sdk/clis/aptos/src/node/analyze/fetch_metadata.rs create mode 100644 movement-sdk/clis/aptos/src/node/analyze/mod.rs create mode 100644 movement-sdk/clis/aptos/src/node/mod.rs create mode 100644 movement-sdk/clis/aptos/src/op/key.rs create mode 100644 movement-sdk/clis/aptos/src/op/mod.rs create mode 100644 movement-sdk/clis/aptos/src/stake/mod.rs create mode 100644 movement-sdk/clis/aptos/src/test/mod.rs create mode 100644 movement-sdk/clis/aptos/src/test/tests.rs create mode 100644 movement-sdk/clis/aptos/src/update/helpers.rs create mode 100644 movement-sdk/clis/aptos/src/update/mod.rs create mode 100644 movement-sdk/clis/aptos/src/update/tool.rs create mode 100644 movement-sdk/clis/movement/src/aptos/aptos.rs create mode 100644 movement-sdk/clis/movement/src/sui/sui.rs create mode 100644 movement-sdk/rust-toolchain diff --git a/canonical/Cargo.lock b/canonical/Cargo.lock index ecdd33339..a281a151d 100644 --- a/canonical/Cargo.lock +++ b/canonical/Cargo.lock @@ -315,7 +315,7 @@ dependencies = [ "aptos-metrics-core", "aptos-types", "bcs 0.1.4", - "clap 4.4.7", + "clap 4.4.10", "dashmap", "itertools 0.10.5", "move-core-types 0.0.4-canonical-aptos", @@ -391,7 +391,7 @@ dependencies = [ "better_any", "blake2-rfc", "blst", - "clap 4.4.7", + "clap 4.4.10", "codespan-reporting", "curve25519-dalek", "flate2", @@ -447,7 +447,7 @@ dependencies = [ "aptos-types", "aptos-vm-types", "bcs 0.1.4", - "clap 4.4.7", + "clap 4.4.10", "move-binary-format 0.0.3-canonical-aptos", "move-core-types 0.0.4-canonical-aptos", "move-model 0.1.0-canonical-aptos", @@ -602,7 +602,7 @@ dependencies = [ "anyhow", "aptos-types", "bcs 0.1.4", - "clap 4.4.7", + "clap 4.4.10", "heck 0.3.3", "move-core-types 0.0.4-canonical-aptos", "once_cell", @@ -2068,9 +2068,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.7" +version = "4.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" dependencies = [ "clap_builder", "clap_derive", @@ -2078,9 +2078,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.7" +version = "4.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" dependencies = [ "anstream", "anstyle", @@ -5201,7 +5201,7 @@ version = "0.0.1-canonical-aptos" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.7", + "clap 4.4.10", "codespan-reporting", "difference", "hex", @@ -5229,7 +5229,7 @@ version = "0.0.1-canonical-sui" dependencies = [ "anyhow", "bcs 0.1.6", - "clap 4.4.7", + "clap 4.4.10", "codespan-reporting", "hex", "move-binary-format 0.0.3-canonical-sui", @@ -5292,7 +5292,7 @@ version = "0.1.0-canonical-aptos" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.7", + "clap 4.4.10", "codespan", "colored", "move-binary-format 0.0.3-canonical-aptos", @@ -5311,7 +5311,7 @@ version = "0.1.0-canonical-sui" dependencies = [ "anyhow", "bcs 0.1.6", - "clap 4.4.7", + "clap 4.4.10", "codespan", "colored", "move-binary-format 0.0.3-canonical-sui", @@ -5328,7 +5328,7 @@ name = "move-disassembler" version = "0.1.0-canonical-aptos" dependencies = [ "anyhow", - "clap 4.4.7", + "clap 4.4.10", "colored", "move-binary-format 0.0.3-canonical-aptos", "move-bytecode-source-map 0.1.0-canonical-aptos", @@ -5346,7 +5346,7 @@ version = "0.1.0-canonical-sui" dependencies = [ "anyhow", "bcs 0.1.6", - "clap 4.4.7", + "clap 4.4.10", "colored", "hex", "move-binary-format 0.0.3-canonical-sui", @@ -5557,7 +5557,7 @@ version = "0.1.0-canonical-aptos" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.7", + "clap 4.4.10", "colored", "dirs-next", "itertools 0.10.5", @@ -5591,7 +5591,7 @@ name = "move-package" version = "0.1.0-canonical-sui" dependencies = [ "anyhow", - "clap 4.4.7", + "clap 4.4.10", "colored", "move-abigen 0.1.0-canonical-sui", "move-binary-format 0.0.3-canonical-sui", @@ -5633,7 +5633,7 @@ dependencies = [ "anyhow", "async-trait", "atty", - "clap 4.4.7", + "clap 4.4.10", "codespan", "codespan-reporting", "futures 0.3.28", @@ -5667,7 +5667,7 @@ name = "move-prover" version = "0.1.0-canonical-sui" dependencies = [ "anyhow", - "clap 4.4.7", + "clap 4.4.10", "codespan-reporting", "itertools 0.10.5", "log", @@ -6375,7 +6375,7 @@ dependencies = [ "axum", "bytes", "cfg-if", - "clap 4.4.7", + "clap 4.4.10", "eyre", "fastcrypto", "futures 0.3.28", @@ -10127,7 +10127,7 @@ dependencies = [ name = "sui-protocol-config" version = "0.1.0-canonical-sui" dependencies = [ - "clap 4.4.7", + "clap 4.4.10", "insta", "schemars", "serde 1.0.190", @@ -10154,7 +10154,7 @@ dependencies = [ "anyhow", "async-trait", "bcs 0.1.6", - "clap 4.4.7", + "clap 4.4.10", "colored", "fastcrypto", "futures 0.3.28", @@ -10213,7 +10213,7 @@ dependencies = [ "byteorder", "bytes", "chrono", - "clap 4.4.7", + "clap 4.4.10", "eyre", "fastcrypto", "futures 0.3.28", @@ -10531,7 +10531,7 @@ dependencies = [ "atomic_float", "bytes", "bytes-varint", - "clap 4.4.7", + "clap 4.4.10", "crossterm", "futures 0.3.28", "once_cell", diff --git a/movement-sdk/Cargo.lock b/movement-sdk/Cargo.lock index cddcb3167..96b165922 100644 --- a/movement-sdk/Cargo.lock +++ b/movement-sdk/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + [[package]] name = "addr2line" version = "0.21.0" @@ -18,3094 +24,15270 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] -name = "aho-corasick" -version = "1.1.2" +name = "aead" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "memchr", + "crypto-common", + "generic-array", ] [[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" +name = "aes" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ - "libc", + "cfg-if", + "cipher", + "cpufeatures", ] [[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - -[[package]] -name = "asn1-rs" -version = "0.5.2" +name = "aes-gcm" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror", - "time", + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", ] [[package]] -name = "asn1-rs-derive" -version = "0.4.0" +name = "ahash" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure", + "getrandom 0.2.11", + "once_cell", + "version_check", ] [[package]] -name = "asn1-rs-impl" -version = "0.1.0" +name = "ahash" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "cfg-if", + "getrandom 0.2.11", + "once_cell", + "version_check", + "zerocopy", ] [[package]] -name = "async-stream" -version = "0.3.5" +name = "aho-corasick" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", + "memchr", ] [[package]] -name = "async-stream-impl" -version = "0.3.5" +name = "aliasable" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", -] +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" [[package]] -name = "async-trait" -version = "0.1.74" +name = "alloc-no-stdlib" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", -] +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" [[package]] -name = "atty" -version = "0.2.14" +name = "alloc-stdlib" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", + "alloc-no-stdlib", ] [[package]] -name = "auto_impl" -version = "1.1.0" +name = "android-tzdata" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] -name = "autocfg" -version = "1.1.0" +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] [[package]] -name = "avalanche-types" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "768d08fdaf7a9fcd16062f00af5985a729357bbdedb83eb5a8f196424c0186fe" +name = "anemo" +version = "0.0.0" +source = "git+https://github.com/mystenlabs/anemo.git?rev=1169850e6af127397068cd86764c29b1d49dbe35#1169850e6af127397068cd86764c29b1d49dbe35" dependencies = [ + "anyhow", "async-trait", - "base64 0.21.5", - "bech32", - "blst", - "bs58 0.5.0", + "bincode", "bytes", - "cert-manager", - "chrono", - "cmp-manager", - "ecdsa", - "ethers-core", + "ed25519", "futures", "hex", - "hmac", "http", - "hyper", - "jsonrpc-core", - "k256", - "lazy_static", - "log", - "num-derive", - "num-traits", - "prefix-manager", - "primitive-types", - "prost", - "rand", + "matchit 0.5.0", + "pin-project-lite", + "pkcs8 0.9.0", + "quinn", + "quinn-proto", + "rand 0.8.5", + "rcgen", "ring 0.16.20", - "ripemd", - "rust-embed", - "semver", - "serde", + "rustls 0.21.9", + "rustls-webpki", + "serde 1.0.192", "serde_json", - "serde_with", - "serde_yaml", - "sha2", - "sha3", - "spki", - "strum", + "socket2 0.5.5", + "tap", "thiserror", "tokio", - "tokio-stream", - "tonic", - "tonic-health", - "tonic-reflection", - "tower-service", - "url", - "zerocopy", - "zeroize", + "tokio-util 0.7.10", + "tower", + "tracing", + "x509-parser", ] [[package]] -name = "axum" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +name = "anemo-build" +version = "0.0.0" +source = "git+https://github.com/mystenlabs/anemo.git?rev=1169850e6af127397068cd86764c29b1d49dbe35#1169850e6af127397068cd86764c29b1d49dbe35" dependencies = [ - "async-trait", - "axum-core", - "bitflags 1.3.2", + "prettyplease 0.1.25", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "anemo-tower" +version = "0.0.0" +source = "git+https://github.com/mystenlabs/anemo.git?rev=1169850e6af127397068cd86764c29b1d49dbe35#1169850e6af127397068cd86764c29b1d49dbe35" +dependencies = [ + "anemo", "bytes", - "futures-util", - "http", - "http-body", - "hyper", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", + "dashmap", + "futures", + "governor", + "nonzero_ext", "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper", + "tokio", "tower", - "tower-layer", - "tower-service", + "tracing", + "uuid", ] [[package]] -name = "axum-core" -version = "0.3.4" +name = "ansi-escapes" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "mime", - "rustversion", - "tower-layer", - "tower-service", -] +checksum = "7e3c0daaaae24df5995734b689627f8fa02101bc5bbc768be3055b66a010d7af" [[package]] -name = "backtrace" -version = "0.3.69" +name = "ansi_term" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", + "winapi 0.3.9", ] [[package]] -name = "base16ct" -version = "0.2.0" +name = "anstream" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] [[package]] -name = "base64" -version = "0.13.1" +name = "anstyle" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] -name = "base64" -version = "0.21.5" +name = "anstyle-parse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] [[package]] -name = "base64ct" -version = "1.6.0" +name = "anstyle-query" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] [[package]] -name = "bech32" -version = "0.9.1" +name = "anstyle-wincon" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] [[package]] -name = "bitflags" -version = "1.3.2" +name = "anyhow" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +dependencies = [ + "backtrace", +] [[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +name = "aptos" +version = "2.0.2-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-api-types", + "aptos-backup-cli", + "aptos-bitvec", + "aptos-build-info", + "aptos-cached-packages", + "aptos-cli-common", + "aptos-config", + "aptos-crypto", + "aptos-db-tool", + "aptos-debugger", + "aptos-faucet-core", + "aptos-framework", + "aptos-gas", + "aptos-gas-profiling", + "aptos-genesis", + "aptos-github-client", + "aptos-global-constants", + "aptos-keygen", + "aptos-logger", + "aptos-network-checker", + "aptos-node", + "aptos-rest-client", + "aptos-sdk", + "aptos-storage-interface", + "aptos-telemetry", + "aptos-temppath", + "aptos-transactional-test-harness", + "aptos-types", + "aptos-vm", + "aptos-vm-genesis", + "async-trait", + "base64 0.13.1", + "bcs 0.1.4", + "chrono", + "clap 4.4.10", + "clap_complete", + "codespan-reporting", + "dirs 5.0.1", + "futures", + "hex", + "itertools 0.10.5", + "jemallocator", + "move-binary-format 0.0.3-canonical-aptos", + "move-bytecode-source-map 0.1.0-canonical-aptos", + "move-cli 0.1.0-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-compiler 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-coverage 0.1.0-canonical-aptos", + "move-disassembler 0.1.0-canonical-aptos", + "move-ir-compiler", + "move-ir-types 0.1.0-canonical-aptos", + "move-package 0.1.0-canonical-aptos", + "move-prover 0.1.0-canonical-aptos", + "move-prover-boogie-backend 0.1.0-canonical-aptos", + "move-symbol-pool 0.1.0-canonical-aptos", + "move-unit-test 0.1.0-canonical-aptos", + "move-vm-runtime 0.1.0-canonical-aptos", + "once_cell", + "rand 0.7.3", + "regex", + "reqwest", + "self_update", + "serde 1.0.192", + "serde_json", + "serde_yaml 0.8.26", + "shadow-rs", + "tempfile", + "termcolor", + "thiserror", + "tokio", + "tokio-util 0.7.10", + "toml 0.7.8", + "walkdir", +] [[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +name = "aptos-accumulator" +version = "0.1.0-canonical-aptos" dependencies = [ - "funty", - "radium", - "tap", - "wyz", + "anyhow", + "aptos-crypto", + "aptos-types", ] [[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +name = "aptos-aggregator" +version = "0.1.0-canonical-aptos" dependencies = [ - "generic-array", + "anyhow", + "aptos-crypto", + "aptos-state-view", + "aptos-types", + "bcs 0.1.4", + "better_any", + "move-binary-format 0.0.3-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-table-extension", + "once_cell", + "smallvec", ] [[package]] -name = "blst" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94087b935a822949d3291a9989ad2b2051ea141eda0fd4e478a75f6aa3e604b" +name = "aptos-api" +version = "0.2.0-canonical-aptos" dependencies = [ - "cc", - "glob", - "threadpool", - "zeroize", + "anyhow", + "aptos-api-types", + "aptos-build-info", + "aptos-config", + "aptos-crypto", + "aptos-gas", + "aptos-logger", + "aptos-mempool", + "aptos-metrics-core", + "aptos-runtimes", + "aptos-state-view", + "aptos-storage-interface", + "aptos-types", + "aptos-vm", + "async-trait", + "bcs 0.1.4", + "bytes", + "fail 0.5.1", + "futures", + "hex", + "hyper", + "itertools 0.10.5", + "mime", + "move-core-types 0.0.4-canonical-aptos", + "num_cpus", + "once_cell", + "paste", + "poem", + "poem-openapi", + "regex", + "serde 1.0.192", + "serde_json", + "tokio", + "url", ] [[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +name = "aptos-api-types" +version = "0.0.1-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-config", + "aptos-crypto", + "aptos-framework", + "aptos-logger", + "aptos-openapi", + "aptos-storage-interface", + "aptos-types", + "aptos-vm", + "async-trait", + "bcs 0.1.4", + "hex", + "indoc", + "move-binary-format 0.0.3-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-resource-viewer", + "poem", + "poem-openapi", + "serde 1.0.192", + "serde_json", +] [[package]] -name = "bs58" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +name = "aptos-backup-cli" +version = "0.1.0-canonical-aptos" dependencies = [ - "sha2", - "tinyvec", + "anyhow", + "aptos-backup-service", + "aptos-config", + "aptos-crypto", + "aptos-db", + "aptos-executor", + "aptos-executor-test-helpers", + "aptos-executor-types", + "aptos-infallible", + "aptos-jellyfish-merkle", + "aptos-logger", + "aptos-proptest-helpers", + "aptos-push-metrics", + "aptos-scratchpad", + "aptos-storage-interface", + "aptos-temppath", + "aptos-types", + "aptos-vm", + "async-trait", + "bcs 0.1.4", + "bytes", + "clap 4.4.10", + "csv", + "futures", + "itertools 0.10.5", + "move-binary-format 0.0.3-canonical-aptos", + "move-bytecode-verifier 0.1.0-canonical-aptos", + "num_cpus", + "once_cell", + "pin-project", + "rand 0.7.3", + "regex", + "reqwest", + "serde 1.0.192", + "serde_json", + "serde_yaml 0.8.26", + "tokio", + "tokio-io-timeout", + "tokio-stream", + "tokio-util 0.7.10", ] [[package]] -name = "bumpalo" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +name = "aptos-backup-service" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-crypto", + "aptos-db", + "aptos-logger", + "aptos-metrics-core", + "aptos-runtimes", + "aptos-storage-interface", + "aptos-types", + "bcs 0.1.4", + "bytes", + "hyper", + "once_cell", + "serde 1.0.192", + "tokio", + "warp", +] [[package]] -name = "byte-slice-cast" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +name = "aptos-bitvec" +version = "0.1.0-canonical-aptos" +dependencies = [ + "serde 1.0.192", + "serde_bytes", +] [[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +name = "aptos-block-executor" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-aggregator", + "aptos-infallible", + "aptos-logger", + "aptos-metrics-core", + "aptos-mvhashmap", + "aptos-state-view", + "aptos-types", + "aptos-vm-logging", + "arc-swap", + "bcs 0.1.4", + "crossbeam", + "dashmap", + "move-binary-format 0.0.3-canonical-aptos", + "num_cpus", + "once_cell", + "parking_lot 0.12.1", + "rayon", +] [[package]] -name = "bytes" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +name = "aptos-block-partitioner" +version = "0.1.0-canonical-aptos" dependencies = [ - "serde", + "anyhow", + "aptos-crypto", + "aptos-logger", + "aptos-metrics-core", + "aptos-types", + "bcs 0.1.4", + "clap 4.4.10", + "dashmap", + "itertools 0.10.5", + "move-core-types 0.0.4-canonical-aptos", + "once_cell", + "rand 0.7.3", + "rayon", ] [[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +name = "aptos-bounded-executor" +version = "0.1.0-canonical-aptos" dependencies = [ - "libc", + "futures", + "tokio", ] [[package]] -name = "cert-manager" -version = "0.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "166da6fda67aa44381158b8ad9c3b28d842951791c431ce61333e62cb9b05d5b" +name = "aptos-build-info" +version = "0.1.0-canonical-aptos" dependencies = [ - "log", - "rand", - "random-manager", - "rcgen", - "rsa", - "rustls", - "rustls-pemfile", - "x509-parser 0.15.1", + "shadow-rs", ] [[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +name = "aptos-cached-packages" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-framework", + "aptos-types", + "bcs 0.1.4", + "include_dir 0.7.3", + "move-core-types 0.0.4-canonical-aptos", + "once_cell", +] [[package]] -name = "channel-avalanche" -version = "0.1.0" +name = "aptos-channels" +version = "0.1.0-canonical-aptos" dependencies = [ "anyhow", - "async-trait", - "avalanche-types", - "ctor", - "movement-sdk", - "serde", - "serde_json", - "tokio", + "aptos-infallible", + "aptos-logger", + "aptos-metrics-core", + "futures", ] [[package]] -name = "chrono" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +name = "aptos-cli-common" +version = "1.0.0-canonical-aptos" dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets", + "anstyle", + "clap 4.4.10", + "clap_complete", ] [[package]] -name = "clap" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +name = "aptos-compression" +version = "0.1.0-canonical-aptos" dependencies = [ - "atty", - "bitflags 1.3.2", - "clap_derive", - "clap_lex", - "indexmap 1.9.3", + "aptos-logger", + "aptos-metrics-core", + "lz4", "once_cell", - "strsim", - "termcolor", - "textwrap", + "thiserror", ] [[package]] -name = "clap_derive" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" +name = "aptos-config" +version = "0.1.0-canonical-aptos" dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", + "anyhow", + "aptos-crypto", + "aptos-crypto-derive", + "aptos-global-constants", + "aptos-logger", + "aptos-secure-storage", + "aptos-short-hex-str", + "aptos-temppath", + "aptos-types", + "bcs 0.1.4", + "byteorder", + "cfg-if", + "cfg_block", + "get_if_addrs", + "mirai-annotations", + "num_cpus", + "poem-openapi", + "rand 0.7.3", + "serde 1.0.192", + "serde_merge", + "serde_yaml 0.8.26", + "thiserror", + "url", ] [[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +name = "aptos-consensus" +version = "0.1.0-canonical-aptos" dependencies = [ - "os_str_bytes", + "anyhow", + "aptos-bitvec", + "aptos-bounded-executor", + "aptos-channels", + "aptos-config", + "aptos-consensus-notifications", + "aptos-consensus-types", + "aptos-crypto", + "aptos-crypto-derive", + "aptos-enum-conversion-derive", + "aptos-event-notifications", + "aptos-executor", + "aptos-executor-types", + "aptos-fallible", + "aptos-global-constants", + "aptos-infallible", + "aptos-logger", + "aptos-mempool", + "aptos-metrics-core", + "aptos-network", + "aptos-runtimes", + "aptos-safety-rules", + "aptos-schemadb", + "aptos-secure-storage", + "aptos-short-hex-str", + "aptos-storage-interface", + "aptos-temppath", + "aptos-types", + "aptos-vm", + "arc-swap", + "async-trait", + "bcs 0.1.4", + "byteorder", + "bytes", + "chrono", + "claims", + "dashmap", + "fail 0.5.1", + "futures", + "futures-channel", + "itertools 0.10.5", + "maplit", + "mirai-annotations", + "move-core-types 0.0.4-canonical-aptos", + "num-derive", + "num-traits 0.2.17", + "once_cell", + "rand 0.7.3", + "rayon", + "serde 1.0.192", + "serde_bytes", + "serde_json", + "thiserror", + "tokio", + "tokio-metrics", ] [[package]] -name = "cmp-manager" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f5e2b424191b35b798b06e6c67fa5a5440a098925d931d7e91511d7d8fe275" +name = "aptos-consensus-notifications" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-crypto", + "aptos-runtimes", + "aptos-types", + "async-trait", + "futures", + "serde 1.0.192", + "thiserror", + "tokio", +] [[package]] -name = "const-oid" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +name = "aptos-consensus-types" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-bitvec", + "aptos-crypto", + "aptos-crypto-derive", + "aptos-executor-types", + "aptos-infallible", + "aptos-short-hex-str", + "aptos-types", + "bcs 0.1.4", + "futures", + "itertools 0.10.5", + "mirai-annotations", + "rand 0.7.3", + "rayon", + "serde 1.0.192", + "tokio", +] [[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +name = "aptos-crash-handler" +version = "0.1.0-canonical-aptos" dependencies = [ - "core-foundation-sys", - "libc", + "aptos-logger", + "backtrace", + "move-core-types 0.0.4-canonical-aptos", + "serde 1.0.192", + "toml 0.7.8", ] [[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +name = "aptos-crypto" +version = "0.0.3-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-crypto-derive", + "ark-ec", + "ark-ff", + "ark-std", + "bcs 0.1.4", + "blst", + "bytes", + "curve25519-dalek", + "digest 0.9.0", + "ed25519-dalek", + "hex", + "hkdf 0.10.0", + "libsecp256k1", + "more-asserts", + "once_cell", + "proptest", + "proptest-derive", + "rand 0.7.3", + "rand_core 0.5.1", + "ring 0.16.20", + "serde 1.0.192", + "serde-name 0.1.2", + "serde_bytes", + "sha2 0.10.8", + "sha2 0.9.9", + "static_assertions", + "thiserror", + "tiny-keccak", + "x25519-dalek", +] [[package]] -name = "cpufeatures" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +name = "aptos-crypto-derive" +version = "0.0.3-canonical-aptos" dependencies = [ - "libc", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +name = "aptos-data-client" +version = "0.1.0-canonical-aptos" dependencies = [ - "cfg-if", + "aptos-config", + "aptos-crypto", + "aptos-id-generator", + "aptos-infallible", + "aptos-logger", + "aptos-metrics-core", + "aptos-netcore", + "aptos-network", + "aptos-storage-interface", + "aptos-storage-service-client", + "aptos-storage-service-types", + "aptos-time-service", + "aptos-types", + "async-trait", + "futures", + "itertools 0.10.5", + "rand 0.7.3", + "serde 1.0.192", + "thiserror", + "tokio", ] [[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +name = "aptos-data-streaming-service" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-channels", + "aptos-config", + "aptos-crypto", + "aptos-data-client", + "aptos-id-generator", + "aptos-infallible", + "aptos-logger", + "aptos-metrics-core", + "aptos-network", + "aptos-short-hex-str", + "aptos-types", + "async-trait", + "enum_dispatch", + "futures", + "once_cell", + "serde 1.0.192", + "thiserror", + "tokio", + "tokio-stream", +] [[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +name = "aptos-db" +version = "0.1.0-canonical-aptos" dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", + "anyhow", + "aptos-accumulator", + "aptos-config", + "aptos-crypto", + "aptos-db-indexer", + "aptos-executor-types", + "aptos-infallible", + "aptos-jellyfish-merkle", + "aptos-logger", + "aptos-metrics-core", + "aptos-proptest-helpers", + "aptos-rocksdb-options", + "aptos-schemadb", + "aptos-scratchpad", + "aptos-state-view", + "aptos-storage-interface", + "aptos-temppath", + "aptos-types", + "aptos-vm", + "arc-swap", + "arr_macro", + "bcs 0.1.4", + "byteorder", + "claims", + "clap 4.4.10", + "dashmap", + "itertools 0.10.5", + "lru 0.7.8", + "move-core-types 0.0.4-canonical-aptos", + "move-resource-viewer", + "num-derive", + "num_cpus", + "once_cell", + "owo-colors", + "proptest", + "proptest-derive", + "rayon", + "serde 1.0.192", + "static_assertions", + "status-line", + "thiserror", ] [[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +name = "aptos-db-indexer" +version = "0.1.0-canonical-aptos" dependencies = [ - "generic-array", - "typenum", + "anyhow", + "aptos-config", + "aptos-crypto", + "aptos-infallible", + "aptos-logger", + "aptos-metrics-core", + "aptos-rocksdb-options", + "aptos-schemadb", + "aptos-scratchpad", + "aptos-state-view", + "aptos-storage-interface", + "aptos-types", + "aptos-vm", + "bcs 0.1.4", + "byteorder", + "move-core-types 0.0.4-canonical-aptos", + "move-resource-viewer", + "num-derive", + "serde 1.0.192", ] [[package]] -name = "ctor" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e366bff8cd32dd8754b0991fb66b279dc48f598c3a18914852a6673deef583" +name = "aptos-db-tool" +version = "0.1.0-canonical-aptos" dependencies = [ - "quote", - "syn 2.0.38", + "anyhow", + "aptos-backup-cli", + "aptos-backup-service", + "aptos-config", + "aptos-db", + "aptos-executor-types", + "aptos-logger", + "aptos-push-metrics", + "aptos-state-view", + "aptos-storage-interface", + "aptos-temppath", + "aptos-types", + "async-trait", + "clap 4.4.10", + "itertools 0.10.5", + "owo-colors", + "tokio", ] [[package]] -name = "darling" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +name = "aptos-debugger" +version = "0.1.0-canonical-aptos" dependencies = [ - "darling_core", - "darling_macro", + "anyhow", + "aptos-crypto", + "aptos-gas", + "aptos-gas-profiling", + "aptos-logger", + "aptos-memory-usage-tracker", + "aptos-resource-viewer", + "aptos-rest-client", + "aptos-state-view", + "aptos-types", + "aptos-validator-interface", + "aptos-vm", + "aptos-vm-logging", + "aptos-vm-types", + "bcs 0.1.4", + "clap 4.4.10", + "move-binary-format 0.0.3-canonical-aptos", + "move-cli 0.1.0-canonical-aptos", + "move-compiler 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-resource-viewer", + "move-table-extension", + "move-vm-runtime 0.1.0-canonical-aptos", + "move-vm-test-utils 0.1.0-canonical-aptos", + "regex", + "tokio", + "url", ] [[package]] -name = "darling_core" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +name = "aptos-enum-conversion-derive" +version = "0.0.3-canonical-aptos" dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.38", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] -name = "darling_macro" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +name = "aptos-event-notifications" +version = "0.1.0-canonical-aptos" dependencies = [ - "darling_core", - "quote", - "syn 2.0.38", + "aptos-channels", + "aptos-id-generator", + "aptos-infallible", + "aptos-state-view", + "aptos-storage-interface", + "aptos-types", + "async-trait", + "futures", + "serde 1.0.192", + "thiserror", ] [[package]] -name = "data-encoding" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +name = "aptos-executor" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-block-partitioner", + "aptos-consensus-types", + "aptos-crypto", + "aptos-executor-types", + "aptos-infallible", + "aptos-logger", + "aptos-metrics-core", + "aptos-scratchpad", + "aptos-secure-net", + "aptos-state-view", + "aptos-storage-interface", + "aptos-types", + "aptos-vm", + "arr_macro", + "bcs 0.1.4", + "dashmap", + "fail 0.5.1", + "itertools 0.10.5", + "move-core-types 0.0.4-canonical-aptos", + "num_cpus", + "once_cell", + "rayon", + "serde 1.0.192", +] [[package]] -name = "der" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +name = "aptos-executor-test-helpers" +version = "0.1.0-canonical-aptos" dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", + "anyhow", + "aptos-cached-packages", + "aptos-config", + "aptos-consensus-types", + "aptos-crypto", + "aptos-db", + "aptos-executor", + "aptos-executor-types", + "aptos-genesis", + "aptos-sdk", + "aptos-state-view", + "aptos-storage-interface", + "aptos-temppath", + "aptos-types", + "aptos-vm", + "aptos-vm-genesis", + "rand 0.7.3", +] + +[[package]] +name = "aptos-executor-types" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-block-partitioner", + "aptos-crypto", + "aptos-scratchpad", + "aptos-secure-net", + "aptos-state-view", + "aptos-storage-interface", + "aptos-types", + "bcs 0.1.4", + "dashmap", + "itertools 0.10.5", + "once_cell", + "serde 1.0.192", + "thiserror", ] [[package]] -name = "der-parser" -version = "8.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +name = "aptos-fallible" +version = "0.1.0-canonical-aptos" dependencies = [ - "asn1-rs", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", + "thiserror", ] [[package]] -name = "deranged" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +name = "aptos-faucet-core" +version = "2.0.1-canonical-aptos" dependencies = [ - "powerfmt", - "serde", + "anyhow", + "aptos-config", + "aptos-faucet-metrics-server", + "aptos-logger", + "aptos-metrics-core", + "aptos-sdk", + "async-trait", + "captcha", + "clap 4.4.10", + "deadpool-redis", + "enum_dispatch", + "futures", + "hex", + "ipnet", + "iprange", + "lru 0.9.0", + "once_cell", + "poem", + "poem-openapi", + "rand 0.7.3", + "redis", + "reqwest", + "serde 1.0.192", + "serde_json", + "serde_yaml 0.8.26", + "tokio", + "url", ] [[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +name = "aptos-faucet-metrics-server" +version = "2.0.0-canonical-aptos" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "anyhow", + "aptos-logger", + "aptos-metrics-core", + "once_cell", + "poem", + "prometheus", + "serde 1.0.192", + "serde_json", ] [[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +name = "aptos-framework" +version = "0.1.0-canonical-aptos" dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", + "anyhow", + "aptos-aggregator", + "aptos-crypto", + "aptos-gas-algebra-ext", + "aptos-move-stdlib", + "aptos-sdk-builder", + "aptos-state-view", + "aptos-types", + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", + "base64 0.13.1", + "bcs 0.1.4", + "better_any", + "blake2-rfc", + "blst", + "clap 4.4.10", + "codespan-reporting", + "curve25519-dalek", + "flate2", + "hex", + "include_dir 0.7.3", + "itertools 0.10.5", + "libsecp256k1", + "log", + "move-binary-format 0.0.3-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-compiler 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-docgen 0.1.0-canonical-aptos", + "move-model 0.1.0-canonical-aptos", + "move-package 0.1.0-canonical-aptos", + "move-prover 0.1.0-canonical-aptos", + "move-prover-boogie-backend 0.1.0-canonical-aptos", + "move-stackless-bytecode 0.1.0-canonical-aptos", + "move-table-extension", + "move-vm-runtime 0.1.0-canonical-aptos", + "move-vm-types 0.1.0-canonical-aptos", + "num-traits 0.2.17", + "once_cell", + "rand 0.7.3", + "rand_core 0.5.1", + "rayon", + "ripemd", + "serde 1.0.192", + "serde_bytes", + "serde_json", + "serde_yaml 0.8.26", + "sha2 0.10.8", + "sha2 0.9.9", + "sha3 0.9.1", + "siphasher", + "smallvec", + "tempfile", + "thiserror", + "tiny-keccak", ] [[package]] -name = "dirs" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +name = "aptos-gas" +version = "0.1.0-canonical-aptos" dependencies = [ - "dirs-sys", + "anyhow", + "aptos-framework", + "aptos-gas-algebra-ext", + "aptos-global-constants", + "aptos-logger", + "aptos-move-stdlib", + "aptos-package-builder", + "aptos-types", + "aptos-vm-types", + "bcs 0.1.4", + "clap 4.4.10", + "move-binary-format 0.0.3-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-model 0.1.0-canonical-aptos", + "move-table-extension", + "move-vm-types 0.1.0-canonical-aptos", ] [[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +name = "aptos-gas-algebra-ext" +version = "0.0.1-canonical-aptos" dependencies = [ - "libc", - "redox_users", - "winapi", + "move-core-types 0.0.4-canonical-aptos", ] [[package]] -name = "displaydoc" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +name = "aptos-gas-profiling" +version = "0.1.0-canonical-aptos" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", + "anyhow", + "aptos-framework", + "aptos-gas", + "aptos-package-builder", + "aptos-types", + "inferno", + "move-binary-format 0.0.3-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-vm-types 0.1.0-canonical-aptos", + "regex", + "smallvec", ] [[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +name = "aptos-genesis" +version = "0.1.0-canonical-aptos" dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", + "anyhow", + "aptos-cached-packages", + "aptos-config", + "aptos-crypto", + "aptos-db", + "aptos-executor", + "aptos-framework", + "aptos-keygen", + "aptos-logger", + "aptos-state-view", + "aptos-storage-interface", + "aptos-temppath", + "aptos-types", + "aptos-vm", + "aptos-vm-genesis", + "bcs 0.1.4", + "rand 0.7.3", + "serde 1.0.192", + "serde_yaml 0.8.26", +] + +[[package]] +name = "aptos-github-client" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-proxy", + "serde 1.0.192", + "serde_json", + "thiserror", + "ureq", ] [[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +name = "aptos-global-constants" +version = "0.1.0-canonical-aptos" [[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", - "ff", - "generic-array", - "group", - "pkcs8", - "rand_core", - "sec1", - "subtle", - "zeroize", -] +name = "aptos-id-generator" +version = "0.1.0-canonical-aptos" [[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +name = "aptos-indexer-grpc-fullnode" +version = "1.0.0-canonical-aptos" dependencies = [ - "cfg-if", + "anyhow", + "aptos-api", + "aptos-api-types", + "aptos-bitvec", + "aptos-config", + "aptos-logger", + "aptos-mempool", + "aptos-metrics-core", + "aptos-moving-average", + "aptos-protos", + "aptos-runtimes", + "aptos-storage-interface", + "aptos-types", + "aptos-vm", + "base64 0.13.1", + "bytes", + "chrono", + "fail 0.5.1", + "futures", + "hex", + "hyper", + "move-binary-format 0.0.3-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-package 0.1.0-canonical-aptos", + "once_cell", + "serde 1.0.192", + "serde_json", + "tokio", + "tokio-stream", + "tonic 0.8.3", ] [[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +name = "aptos-infallible" +version = "0.1.0-canonical-aptos" [[package]] -name = "errno" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" +name = "aptos-inspection-service" +version = "0.1.0-canonical-aptos" dependencies = [ - "libc", - "windows-sys", + "anyhow", + "aptos-build-info", + "aptos-config", + "aptos-infallible", + "aptos-logger", + "aptos-metrics-core", + "aptos-network", + "aptos-runtimes", + "aptos-telemetry", + "futures", + "hyper", + "once_cell", + "prometheus", + "reqwest", + "serde_json", + "sysinfo", + "tokio", ] [[package]] -name = "ethabi" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" +name = "aptos-jellyfish-merkle" +version = "0.1.0-canonical-aptos" dependencies = [ - "ethereum-types", - "hex", + "anyhow", + "aptos-crypto", + "aptos-crypto-derive", + "aptos-infallible", + "aptos-logger", + "aptos-metrics-core", + "aptos-storage-interface", + "aptos-types", + "bcs 0.1.4", + "byteorder", + "itertools 0.10.5", + "num-derive", + "num-traits 0.2.17", "once_cell", - "regex", - "serde", - "serde_json", - "sha3", + "proptest", + "proptest-derive", + "rayon", + "serde 1.0.192", "thiserror", - "uint", ] [[package]] -name = "ethbloom" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +name = "aptos-keygen" +version = "0.1.0-canonical-aptos" dependencies = [ - "crunchy", - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "scale-info", - "tiny-keccak", + "aptos-crypto", + "aptos-types", + "rand 0.7.3", ] [[package]] -name = "ethereum-types" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +name = "aptos-language-e2e-tests" +version = "0.1.0-canonical-aptos" dependencies = [ - "ethbloom", - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "primitive-types", - "scale-info", - "uint", + "anyhow", + "aptos-bitvec", + "aptos-block-executor", + "aptos-cached-packages", + "aptos-crypto", + "aptos-framework", + "aptos-gas", + "aptos-gas-profiling", + "aptos-keygen", + "aptos-memory-usage-tracker", + "aptos-proptest-helpers", + "aptos-state-view", + "aptos-types", + "aptos-vm", + "aptos-vm-genesis", + "aptos-vm-logging", + "bcs 0.1.4", + "goldenfile", + "hex", + "move-binary-format 0.0.3-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-ir-compiler", + "move-table-extension", + "move-vm-types 0.1.0-canonical-aptos", + "num_cpus", + "once_cell", + "proptest", + "proptest-derive", + "rand 0.7.3", + "rayon", + "serde 1.0.192", ] [[package]] -name = "ethers-core" -version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6da5fa198af0d3be20c19192df2bd9590b92ce09a8421e793bec8851270f1b05" +name = "aptos-log-derive" +version = "0.1.0-canonical-aptos" dependencies = [ - "arrayvec", - "bytes", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "aptos-logger" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-infallible", + "aptos-log-derive", + "aptos-node-identity", + "backtrace", "chrono", - "elliptic-curve", - "ethabi", - "generic-array", - "hex", - "k256", - "num_enum", - "open-fastrlp", - "rand", - "rlp", - "serde", + "erased-serde", + "futures", + "hostname", + "once_cell", + "prometheus", + "serde 1.0.192", "serde_json", "strum", - "tempfile", - "thiserror", - "tiny-keccak", - "unicode-xid", + "strum_macros", + "tokio", + "tracing", + "tracing-subscriber", ] [[package]] -name = "fastrand" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +name = "aptos-memory-usage-tracker" +version = "0.1.0-canonical-aptos" dependencies = [ - "rand_core", - "subtle", + "aptos-gas", + "aptos-types", + "move-binary-format 0.0.3-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-vm-types 0.1.0-canonical-aptos", ] [[package]] -name = "fixed-hash" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +name = "aptos-mempool" +version = "0.1.0-canonical-aptos" dependencies = [ - "byteorder", - "rand", - "rustc-hex", - "static_assertions", + "anyhow", + "aptos-bounded-executor", + "aptos-channels", + "aptos-config", + "aptos-consensus-types", + "aptos-crypto", + "aptos-data-client", + "aptos-event-notifications", + "aptos-infallible", + "aptos-logger", + "aptos-mempool-notifications", + "aptos-metrics-core", + "aptos-netcore", + "aptos-network", + "aptos-runtimes", + "aptos-short-hex-str", + "aptos-storage-interface", + "aptos-types", + "aptos-vm-validator", + "async-trait", + "bcs 0.1.4", + "fail 0.5.1", + "futures", + "itertools 0.10.5", + "maplit", + "once_cell", + "rand 0.7.3", + "rayon", + "serde 1.0.192", + "serde_json", + "thiserror", + "tokio", + "tokio-stream", ] [[package]] -name = "flate2" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +name = "aptos-mempool-notifications" +version = "0.1.0-canonical-aptos" dependencies = [ - "crc32fast", - "miniz_oxide", + "aptos-runtimes", + "aptos-types", + "async-trait", + "futures", + "serde 1.0.192", + "thiserror", + "tokio", ] [[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +name = "aptos-memsocket" +version = "0.1.0-canonical-aptos" dependencies = [ - "foreign-types-shared", + "aptos-infallible", + "bytes", + "futures", + "once_cell", ] [[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +name = "aptos-metrics-core" +version = "0.1.0-canonical-aptos" dependencies = [ - "percent-encoding", + "anyhow", + "prometheus", ] +[[package]] +name = "aptos-move-stdlib" +version = "0.1.1-canonical-aptos" +dependencies = [ + "anyhow", + "hex", + "log", + "move-binary-format 0.0.3-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-compiler 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-docgen 0.1.0-canonical-aptos", + "move-errmapgen 0.1.0-canonical-aptos", + "move-prover 0.1.0-canonical-aptos", + "move-vm-runtime 0.1.0-canonical-aptos", + "move-vm-types 0.1.0-canonical-aptos", + "sha2 0.9.9", + "sha3 0.9.1", + "smallvec", + "walkdir", +] + +[[package]] +name = "aptos-moving-average" +version = "0.1.0-canonical-aptos" +dependencies = [ + "chrono", +] + +[[package]] +name = "aptos-mvhashmap" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-aggregator", + "aptos-crypto", + "aptos-infallible", + "aptos-types", + "bcs 0.1.4", + "crossbeam", + "dashmap", +] + +[[package]] +name = "aptos-netcore" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-memsocket", + "aptos-proxy", + "aptos-types", + "bytes", + "futures", + "pin-project", + "serde 1.0.192", + "tokio", + "tokio-util 0.7.10", + "url", +] + +[[package]] +name = "aptos-network" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-bitvec", + "aptos-channels", + "aptos-compression", + "aptos-config", + "aptos-crypto", + "aptos-crypto-derive", + "aptos-id-generator", + "aptos-infallible", + "aptos-logger", + "aptos-metrics-core", + "aptos-netcore", + "aptos-num-variants", + "aptos-peer-monitoring-service-types", + "aptos-rate-limiter", + "aptos-short-hex-str", + "aptos-time-service", + "aptos-types", + "async-trait", + "bcs 0.1.4", + "bytes", + "futures", + "futures-util", + "hex", + "itertools 0.10.5", + "maplit", + "once_cell", + "pin-project", + "rand 0.7.3", + "serde 1.0.192", + "serde_bytes", + "serde_json", + "thiserror", + "tokio", + "tokio-retry", + "tokio-util 0.7.10", +] + +[[package]] +name = "aptos-network-builder" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-channels", + "aptos-config", + "aptos-crypto", + "aptos-event-notifications", + "aptos-infallible", + "aptos-logger", + "aptos-netcore", + "aptos-network", + "aptos-network-discovery", + "aptos-secure-storage", + "aptos-time-service", + "aptos-types", + "async-trait", + "bcs 0.1.4", + "futures", + "maplit", + "rand 0.7.3", + "serde 1.0.192", + "tokio", +] + +[[package]] +name = "aptos-network-checker" +version = "0.0.1-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-config", + "aptos-crypto", + "aptos-logger", + "aptos-network", + "aptos-types", + "clap 4.4.10", + "futures", + "serde 1.0.192", + "tokio", +] + +[[package]] +name = "aptos-network-discovery" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-channels", + "aptos-config", + "aptos-crypto", + "aptos-event-notifications", + "aptos-logger", + "aptos-metrics-core", + "aptos-network", + "aptos-rest-client", + "aptos-secure-storage", + "aptos-short-hex-str", + "aptos-time-service", + "aptos-types", + "bcs 0.1.4", + "futures", + "once_cell", + "serde_yaml 0.8.26", + "tokio", + "url", +] + +[[package]] +name = "aptos-node" +version = "1.5.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-api", + "aptos-backup-service", + "aptos-build-info", + "aptos-cached-packages", + "aptos-channels", + "aptos-config", + "aptos-consensus", + "aptos-consensus-notifications", + "aptos-crash-handler", + "aptos-crypto", + "aptos-data-client", + "aptos-data-streaming-service", + "aptos-db", + "aptos-event-notifications", + "aptos-executor", + "aptos-executor-types", + "aptos-framework", + "aptos-genesis", + "aptos-indexer-grpc-fullnode", + "aptos-infallible", + "aptos-inspection-service", + "aptos-logger", + "aptos-mempool", + "aptos-mempool-notifications", + "aptos-network", + "aptos-network-builder", + "aptos-node-identity", + "aptos-peer-monitoring-service-client", + "aptos-peer-monitoring-service-server", + "aptos-peer-monitoring-service-types", + "aptos-runtimes", + "aptos-secure-storage", + "aptos-state-sync-driver", + "aptos-state-view", + "aptos-storage-interface", + "aptos-storage-service-client", + "aptos-storage-service-notifications", + "aptos-storage-service-server", + "aptos-storage-service-types", + "aptos-telemetry", + "aptos-temppath", + "aptos-time-service", + "aptos-types", + "aptos-vm", + "bcs 0.1.4", + "clap 4.4.10", + "fail 0.5.1", + "futures", + "hex", + "jemallocator", + "maplit", + "rand 0.7.3", + "rayon", + "serde 1.0.192", + "serde_json", + "serde_yaml 0.8.26", + "tokio", + "tokio-stream", + "url", +] + +[[package]] +name = "aptos-node-identity" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-types", + "claims", + "hostname", + "once_cell", +] + +[[package]] +name = "aptos-node-resource-metrics" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-build-info", + "aptos-infallible", + "aptos-logger", + "aptos-metrics-core", + "cfg-if", + "once_cell", + "procfs", + "prometheus", + "sysinfo", +] + +[[package]] +name = "aptos-num-variants" +version = "0.1.0-canonical-aptos" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "aptos-openapi" +version = "0.1.0-canonical-aptos" +dependencies = [ + "async-trait", + "percent-encoding", + "poem", + "poem-openapi", + "serde 1.0.192", + "serde_json", +] + +[[package]] +name = "aptos-package-builder" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-framework", + "itertools 0.10.5", + "move-command-line-common 0.1.0-canonical-aptos", + "move-package 0.1.0-canonical-aptos", + "tempfile", +] + +[[package]] +name = "aptos-peer-monitoring-service-client" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-channels", + "aptos-config", + "aptos-id-generator", + "aptos-infallible", + "aptos-logger", + "aptos-metrics-core", + "aptos-network", + "aptos-peer-monitoring-service-types", + "aptos-time-service", + "aptos-types", + "async-trait", + "enum_dispatch", + "futures", + "once_cell", + "rand 0.7.3", + "serde 1.0.192", + "serde_json", + "thiserror", + "tokio", +] + +[[package]] +name = "aptos-peer-monitoring-service-server" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-bounded-executor", + "aptos-build-info", + "aptos-channels", + "aptos-config", + "aptos-logger", + "aptos-metrics-core", + "aptos-netcore", + "aptos-network", + "aptos-peer-monitoring-service-types", + "aptos-storage-interface", + "aptos-time-service", + "aptos-types", + "bcs 0.1.4", + "bytes", + "cfg_block", + "futures", + "once_cell", + "serde 1.0.192", + "thiserror", + "tokio", +] + +[[package]] +name = "aptos-peer-monitoring-service-types" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-config", + "aptos-types", + "bcs 0.1.4", + "cfg_block", + "serde 1.0.192", + "thiserror", +] + +[[package]] +name = "aptos-proptest-helpers" +version = "0.1.0-canonical-aptos" +dependencies = [ + "crossbeam", + "proptest", + "proptest-derive", +] + +[[package]] +name = "aptos-protos" +version = "1.0.0-canonical-aptos" +dependencies = [ + "pbjson", + "prost 0.11.9", + "serde 1.0.192", + "tonic 0.8.3", +] + +[[package]] +name = "aptos-proxy" +version = "0.1.0-canonical-aptos" +dependencies = [ + "ipnet", +] + +[[package]] +name = "aptos-push-metrics" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-logger", + "aptos-metrics-core", + "ureq", + "url", +] + +[[package]] +name = "aptos-rate-limiter" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-infallible", + "aptos-logger", + "aptos-metrics-core", + "futures", + "pin-project", + "tokio", + "tokio-util 0.7.10", +] + +[[package]] +name = "aptos-resource-viewer" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-types", + "aptos-vm", + "move-core-types 0.0.4-canonical-aptos", + "move-resource-viewer", +] + +[[package]] +name = "aptos-rest-client" +version = "0.0.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-api-types", + "aptos-crypto", + "aptos-infallible", + "aptos-logger", + "aptos-types", + "bcs 0.1.4", + "bytes", + "futures", + "hex", + "move-binary-format 0.0.3-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "poem-openapi", + "reqwest", + "serde 1.0.192", + "serde_json", + "thiserror", + "tokio", + "url", +] + +[[package]] +name = "aptos-rocksdb-options" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-config", + "rocksdb", +] + +[[package]] +name = "aptos-runtimes" +version = "0.1.0-canonical-aptos" +dependencies = [ + "tokio", +] + +[[package]] +name = "aptos-safety-rules" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-config", + "aptos-consensus-types", + "aptos-crypto", + "aptos-global-constants", + "aptos-infallible", + "aptos-logger", + "aptos-metrics-core", + "aptos-secure-net", + "aptos-secure-storage", + "aptos-temppath", + "aptos-types", + "aptos-vault-client", + "once_cell", + "rand 0.7.3", + "serde 1.0.192", + "serde_json", + "thiserror", +] + +[[package]] +name = "aptos-schemadb" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-infallible", + "aptos-logger", + "aptos-metrics-core", + "once_cell", + "proptest", + "rocksdb", +] + +[[package]] +name = "aptos-scratchpad" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-crypto", + "aptos-infallible", + "aptos-metrics-core", + "aptos-types", + "bitvec 1.0.1", + "itertools 0.10.5", + "once_cell", + "proptest", + "rayon", + "thiserror", +] + +[[package]] +name = "aptos-sdk" +version = "0.0.3-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-api-types", + "aptos-cached-packages", + "aptos-crypto", + "aptos-global-constants", + "aptos-rest-client", + "aptos-types", + "bcs 0.1.4", + "ed25519-dalek-bip32", + "move-core-types 0.0.4-canonical-aptos", + "rand_core 0.5.1", + "serde 1.0.192", + "tiny-bip39 0.8.2", +] + +[[package]] +name = "aptos-sdk-builder" +version = "0.2.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-types", + "bcs 0.1.4", + "clap 4.4.10", + "heck 0.3.3", + "move-core-types 0.0.4-canonical-aptos", + "once_cell", + "regex", + "serde-generate", + "serde-reflection 0.3.5", + "serde_yaml 0.8.26", + "textwrap 0.15.2", +] + +[[package]] +name = "aptos-secure-net" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-logger", + "aptos-metrics-core", + "once_cell", + "serde 1.0.192", + "thiserror", +] + +[[package]] +name = "aptos-secure-storage" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-crypto", + "aptos-infallible", + "aptos-logger", + "aptos-temppath", + "aptos-time-service", + "aptos-vault-client", + "base64 0.13.1", + "bcs 0.1.4", + "chrono", + "enum_dispatch", + "rand 0.7.3", + "serde 1.0.192", + "serde_json", + "thiserror", +] + +[[package]] +name = "aptos-short-hex-str" +version = "0.1.0-canonical-aptos" +dependencies = [ + "mirai-annotations", + "serde 1.0.192", + "static_assertions", + "thiserror", +] + +[[package]] +name = "aptos-speculative-state-helper" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-infallible", + "crossbeam", + "once_cell", + "rayon", +] + +[[package]] +name = "aptos-state-sync-driver" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-config", + "aptos-consensus-notifications", + "aptos-crypto", + "aptos-data-client", + "aptos-data-streaming-service", + "aptos-event-notifications", + "aptos-executor-types", + "aptos-infallible", + "aptos-logger", + "aptos-mempool-notifications", + "aptos-metrics-core", + "aptos-runtimes", + "aptos-schemadb", + "aptos-scratchpad", + "aptos-storage-interface", + "aptos-storage-service-notifications", + "aptos-time-service", + "aptos-types", + "async-trait", + "bcs 0.1.4", + "futures", + "once_cell", + "serde 1.0.192", + "thiserror", + "tokio", + "tokio-stream", +] + +[[package]] +name = "aptos-state-view" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-crypto", + "aptos-types", + "bcs 0.1.4", + "serde 1.0.192", + "serde_bytes", + "serde_json", +] + +[[package]] +name = "aptos-storage-interface" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-crypto", + "aptos-logger", + "aptos-metrics-core", + "aptos-scratchpad", + "aptos-secure-net", + "aptos-state-view", + "aptos-types", + "aptos-vm", + "arr_macro", + "bcs 0.1.4", + "crossbeam-channel", + "dashmap", + "itertools 0.10.5", + "move-core-types 0.0.4-canonical-aptos", + "once_cell", + "parking_lot 0.12.1", + "rayon", + "serde 1.0.192", + "thiserror", +] + +[[package]] +name = "aptos-storage-service-client" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-channels", + "aptos-config", + "aptos-network", + "aptos-storage-service-types", + "aptos-types", + "async-trait", + "thiserror", +] + +[[package]] +name = "aptos-storage-service-notifications" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-channels", + "async-trait", + "futures", + "serde 1.0.192", + "thiserror", +] + +[[package]] +name = "aptos-storage-service-server" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-bounded-executor", + "aptos-channels", + "aptos-config", + "aptos-infallible", + "aptos-logger", + "aptos-metrics-core", + "aptos-network", + "aptos-storage-interface", + "aptos-storage-service-notifications", + "aptos-storage-service-types", + "aptos-time-service", + "aptos-types", + "bcs 0.1.4", + "bytes", + "futures", + "lru 0.7.8", + "once_cell", + "serde 1.0.192", + "thiserror", + "tokio", +] + +[[package]] +name = "aptos-storage-service-types" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-compression", + "aptos-config", + "aptos-crypto", + "aptos-types", + "bcs 0.1.4", + "num-traits 0.2.17", + "serde 1.0.192", + "thiserror", +] + +[[package]] +name = "aptos-telemetry" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-api", + "aptos-config", + "aptos-consensus", + "aptos-crypto", + "aptos-db", + "aptos-infallible", + "aptos-logger", + "aptos-mempool", + "aptos-metrics-core", + "aptos-network", + "aptos-node-resource-metrics", + "aptos-runtimes", + "aptos-state-sync-driver", + "aptos-telemetry-service", + "aptos-types", + "flate2", + "futures", + "once_cell", + "prometheus", + "rand 0.7.3", + "rand_core 0.5.1", + "reqwest", + "reqwest-middleware", + "reqwest-retry", + "serde 1.0.192", + "serde_json", + "sysinfo", + "thiserror", + "tokio", + "tokio-retry", + "tokio-stream", + "url", + "uuid", +] + +[[package]] +name = "aptos-telemetry-service" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-config", + "aptos-crypto", + "aptos-crypto-derive", + "aptos-infallible", + "aptos-logger", + "aptos-metrics-core", + "aptos-rest-client", + "aptos-types", + "base64 0.13.1", + "bcs 0.1.4", + "chrono", + "clap 4.4.10", + "debug-ignore", + "flate2", + "futures", + "gcp-bigquery-client", + "hex", + "jsonwebtoken", + "once_cell", + "prometheus", + "rand 0.7.3", + "rand_core 0.5.1", + "reqwest", + "reqwest-middleware", + "reqwest-retry", + "serde 1.0.192", + "serde_json", + "serde_repr", + "serde_yaml 0.8.26", + "thiserror", + "tokio", + "tracing", + "url", + "uuid", + "warp", +] + +[[package]] +name = "aptos-temppath" +version = "0.1.0-canonical-aptos" +dependencies = [ + "hex", + "rand 0.7.3", +] + +[[package]] +name = "aptos-time-service" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-infallible", + "enum_dispatch", + "futures", + "pin-project", + "thiserror", + "tokio", +] + +[[package]] +name = "aptos-transactional-test-harness" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-api-types", + "aptos-cached-packages", + "aptos-crypto", + "aptos-framework", + "aptos-gas", + "aptos-language-e2e-tests", + "aptos-state-view", + "aptos-storage-interface", + "aptos-types", + "aptos-vm", + "aptos-vm-genesis", + "bcs 0.1.4", + "clap 4.4.10", + "hex", + "move-binary-format 0.0.3-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-compiler 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-resource-viewer", + "move-transactional-test-runner", + "move-vm-runtime 0.1.0-canonical-aptos", + "once_cell", + "serde 1.0.192", + "serde_json", +] + +[[package]] +name = "aptos-types" +version = "0.0.3-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-bitvec", + "aptos-crypto", + "aptos-crypto-derive", + "arr_macro", + "bcs 0.1.4", + "chrono", + "derivative", + "hex", + "itertools 0.10.5", + "move-core-types 0.0.4-canonical-aptos", + "move-table-extension", + "num-derive", + "num-traits 0.2.17", + "once_cell", + "proptest", + "proptest-derive", + "rand 0.7.3", + "rayon", + "serde 1.0.192", + "serde_bytes", + "serde_json", + "serde_yaml 0.8.26", + "thiserror", + "tiny-keccak", +] + +[[package]] +name = "aptos-utils" +version = "0.1.0-canonical-aptos" + +[[package]] +name = "aptos-validator-interface" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-api-types", + "aptos-config", + "aptos-db", + "aptos-rest-client", + "aptos-state-view", + "aptos-storage-interface", + "aptos-types", + "async-trait", + "bcs 0.1.4", + "itertools 0.10.5", + "lru 0.7.8", + "move-binary-format 0.0.3-canonical-aptos", + "tokio", +] + +[[package]] +name = "aptos-vault-client" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-crypto", + "base64 0.13.1", + "chrono", + "native-tls", + "once_cell", + "serde 1.0.192", + "serde_json", + "thiserror", + "ureq", +] + +[[package]] +name = "aptos-vm" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-aggregator", + "aptos-block-executor", + "aptos-block-partitioner", + "aptos-crypto", + "aptos-crypto-derive", + "aptos-framework", + "aptos-gas", + "aptos-infallible", + "aptos-logger", + "aptos-memory-usage-tracker", + "aptos-metrics-core", + "aptos-move-stdlib", + "aptos-mvhashmap", + "aptos-state-view", + "aptos-types", + "aptos-utils", + "aptos-vm-logging", + "aptos-vm-types", + "bcs 0.1.4", + "dashmap", + "fail 0.5.1", + "futures", + "move-binary-format 0.0.3-canonical-aptos", + "move-bytecode-utils 0.1.0-canonical-aptos", + "move-bytecode-verifier 0.1.0-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-table-extension", + "move-unit-test 0.1.0-canonical-aptos", + "move-vm-runtime 0.1.0-canonical-aptos", + "move-vm-test-utils 0.1.0-canonical-aptos", + "move-vm-types 0.1.0-canonical-aptos", + "num_cpus", + "once_cell", + "ouroboros 0.15.6", + "rand 0.7.3", + "rayon", + "serde 1.0.192", + "serde_json", + "smallvec", + "tracing", +] + +[[package]] +name = "aptos-vm-genesis" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-cached-packages", + "aptos-crypto", + "aptos-framework", + "aptos-gas", + "aptos-state-view", + "aptos-types", + "aptos-vm", + "bcs 0.1.4", + "move-core-types 0.0.4-canonical-aptos", + "move-vm-types 0.1.0-canonical-aptos", + "once_cell", + "rand 0.7.3", + "serde 1.0.192", +] + +[[package]] +name = "aptos-vm-logging" +version = "0.1.0-canonical-aptos" +dependencies = [ + "aptos-crypto", + "aptos-logger", + "aptos-metrics-core", + "aptos-speculative-state-helper", + "aptos-state-view", + "aptos-types", + "arc-swap", + "once_cell", + "serde 1.0.192", +] + +[[package]] +name = "aptos-vm-types" +version = "0.0.1-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-aggregator", + "aptos-state-view", + "aptos-types", + "move-binary-format 0.0.3-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", +] + +[[package]] +name = "aptos-vm-validator" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "aptos-gas", + "aptos-scratchpad", + "aptos-state-view", + "aptos-storage-interface", + "aptos-types", + "aptos-vm", + "fail 0.5.1", +] + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +dependencies = [ + "serde 1.0.192", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-crypto-primitives" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3a13b34da09176a8baba701233fdffbaa7c1b1192ce031a3da4e55ce1f1a56" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-relations", + "ark-serialize", + "ark-snark", + "ark-std", + "blake2", + "derivative", + "digest 0.10.7", + "sha2 0.10.8", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits 0.2.17", + "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", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits 0.2.17", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote 1.0.33", + "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 0.2.17", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "ark-groth16" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20ceafa83848c3e390f1cbf124bc3193b3e639b3f02009e0e290809a501b95fc" +dependencies = [ + "ark-crypto-primitives", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-relations", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-relations" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00796b6efc05a3f48225e59cb6a2cda78881e7c390872d5786aaf112f31fb4f0" +dependencies = [ + "ark-ff", + "ark-std", + "tracing", +] + +[[package]] +name = "ark-secp256r1" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3975a01b0a6e3eae0f72ec7ca8598a6620fc72fa5981f6f5cca33b7cd788f633" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "ark-snark" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d3cc6833a335bb8a600241889ead68ee89a3cf8448081fb7694c0fe503da63" +dependencies = [ + "ark-ff", + "ark-relations", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits 0.2.17", + "rand 0.8.5", +] + +[[package]] +name = "arr_macro" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a105bfda48707cf19220129e78fca01e9639433ffaef4163546ed8fb04120a5" +dependencies = [ + "arr_macro_impl", + "proc-macro-hack", +] + +[[package]] +name = "arr_macro_impl" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0609c78bd572f4edc74310dfb63a01f5609d53fa8b4dd7c4d98aef3b3e8d72d1" +dependencies = [ + "proc-macro-hack", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +dependencies = [ + "nodrop", +] + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom 7.1.3", + "num-traits 0.2.17", + "rusticata-macros", + "thiserror", + "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 1.0.69", + "quote 1.0.33", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "async-compression" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-recursion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "async-task" +version = "4.3.0" +source = "git+https://github.com/mystenmark/async-task?rev=4e45b26e11126b191701b9b2ce5e2346b8d7682f#4e45b26e11126b191701b9b2ce5e2346b8d7682f" + +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "async_once" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ce4f10ea3abcd6617873bae9f91d1c5332b4a778bd9ce34d0cd517474c1de82" + +[[package]] +name = "atomic_float" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62af46d040ba9df09edc6528dae9d8e49f5f3e82f55b7d2ec31a733c38dbc49d" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "auto_ops" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7460f7dd8e100147b82a63afca1a20eb6c231ee36b90ba7272e14951cb58af59" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "autotools" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8da1805e028a172334c3b680f93e71126f2327622faef2ec3d893c0a4ad77" +dependencies = [ + "cc", +] + +[[package]] +name = "aws-config" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6b3804dca60326e07205179847f17a4fce45af3a1106939177ad41ac08a6de" +dependencies = [ + "aws-credential-types", + "aws-http", + "aws-sdk-sso", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex", + "http", + "hyper", + "ring 0.16.20", + "time", + "tokio", + "tower", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a66ac8ef5fa9cf01c2d999f39d16812e90ec1467bd382cbbb74ba23ea86201" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "fastrand", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-http" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e626370f9ba806ae4c439e49675fd871f5767b093075cdf4fef16cac42ba900" +dependencies = [ + "aws-credential-types", + "aws-smithy-http", + "aws-smithy-types", + "aws-types", + "bytes", + "http", + "http-body", + "lazy_static 1.4.0", + "percent-encoding", + "pin-project-lite", + "tracing", +] + +[[package]] +name = "aws-runtime" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ac5cf0ff19c1bca0cea7932e11b239d1025a45696a4f44f72ea86e2b8bdd07" +dependencies = [ + "aws-credential-types", + "aws-http", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "fastrand", + "http", + "percent-encoding", + "tracing", + "uuid", +] + +[[package]] +name = "aws-sdk-dynamodb" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b4df8750310555fa980f020f152e91013cf83d01036dab992cb64286e11431" +dependencies = [ + "aws-credential-types", + "aws-http", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http", + "regex", + "tokio-stream", + "tracing", +] + +[[package]] +name = "aws-sdk-s3" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e30370b61599168d38190ad272bb91842cd81870a6ca035c05dd5726d22832c" +dependencies = [ + "aws-credential-types", + "aws-http", + "aws-runtime", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-checksums", + "aws-smithy-client", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "http", + "http-body", + "once_cell", + "percent-encoding", + "regex", + "tokio-stream", + "tracing", + "url", +] + +[[package]] +name = "aws-sdk-sso" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "903f888ff190e64f6f5c83fb0f8d54f9c20481f1dc26359bb8896f5d99908949" +dependencies = [ + "aws-credential-types", + "aws-http", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http", + "regex", + "tokio-stream", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47ad6bf01afc00423d781d464220bf69fb6a674ad6629cbbcb06d88cdc2be82" +dependencies = [ + "aws-credential-types", + "aws-http", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "http", + "regex", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b28f4910bb956b7ab320b62e98096402354eca976c587d1eeccd523d9bac03" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-http", + "bytes", + "form_urlencoded", + "hex", + "hmac 0.12.1", + "http", + "once_cell", + "percent-encoding", + "regex", + "sha2 0.10.8", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cdb73f85528b9d19c23a496034ac53703955a59323d581c06aa27b4e4e247af" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", + "tokio-stream", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb15946af1b8d3beeff53ad991d9bff68ac22426b6d40372b958a75fa61eaed" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "crc32c", + "crc32fast", + "hex", + "http", + "http-body", + "md-5 0.10.6", + "pin-project-lite", + "sha1", + "sha2 0.10.8", + "tracing", +] + +[[package]] +name = "aws-smithy-client" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c27b2756264c82f830a91cb4d2d485b2d19ad5bea476d9a966e03d27f27ba59a" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-types", + "bytes", + "fastrand", + "http", + "http-body", + "hyper", + "hyper-rustls 0.24.2", + "lazy_static 1.4.0", + "pin-project-lite", + "rustls 0.21.9", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "850233feab37b591b7377fd52063aa37af615687f5896807abe7f49bd4e1d25b" +dependencies = [ + "aws-smithy-types", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-http" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54cdcf365d8eee60686885f750a34c190e513677db58bbc466c44c588abf4199" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http", + "http-body", + "hyper", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tokio", + "tokio-util 0.7.10", + "tracing", +] + +[[package]] +name = "aws-smithy-http-tower" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "822de399d0ce62829a69dfa8c5cd08efdbe61a7426b953e2268f8b8b52a607bd" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "http", + "http-body", + "pin-project-lite", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1e7ab8fa7ad10c193af7ae56d2420989e9f4758bf03601a342573333ea34f" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-query" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28556a3902091c1f768a34f6c998028921bdab8d47d92586f363f14a4a32d047" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "745e096b3553e7e0f40622aa04971ce52765af82bebdeeac53aa6fc82fe801e6" +dependencies = [ + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand", + "http", + "http-body", + "once_cell", + "pin-project-lite", + "pin-utils", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d0ae0c9cfd57944e9711ea610b48a963fb174a53aabacc08c5794a594b1d02" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "http", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-types" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d90dbc8da2f6be461fa3c1906b20af8f79d14968fe47f2b7d29d086f62a51728" +dependencies = [ + "base64-simd", + "itoa", + "num-integer", + "ryu", + "serde 1.0.192", + "time", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01d2dedcdd8023043716cfeeb3c6c59f2d447fce365d8e194838891794b23b6" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85aa0451bf8af1bf22a4f028d5d28054507a14be43cb8ac0597a8471fba9edfe" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-types", + "http", + "rustc_version", + "tracing", +] + +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "base64 0.21.5", + "bitflags 1.3.2", + "bytes", + "futures-util", + "headers", + "http", + "http-body", + "hyper", + "itoa", + "matchit 0.7.3", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde 1.0.192", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-server" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447f28c85900215cc1bea282f32d4a2f22d55c5a300afdfbc661c8d6a632e063" +dependencies = [ + "arc-swap", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "rustls 0.21.9", + "rustls-pemfile 1.0.4", + "tokio", + "tokio-rustls 0.24.1", + "tower-service", +] + +[[package]] +name = "backoff" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" +dependencies = [ + "futures-core", + "getrandom 0.2.11", + "instant", + "pin-project-lite", + "rand 0.8.5", + "tokio", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "base64-url" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5efc028d778cd6fb4d1779ca001b3282717e34127b726593002506aa77ca08" +dependencies = [ + "base64 0.21.5", +] + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bcs" +version = "0.1.4" +source = "git+https://github.com/aptos-labs/bcs.git?rev=d31fab9d81748e2594be5cd5cdf845786a30562d#d31fab9d81748e2594be5cd5cdf845786a30562d" +dependencies = [ + "serde 1.0.192", + "thiserror", +] + +[[package]] +name = "bcs" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b6598a2f5d564fb7855dc6b06fd1c38cff5a72bd8b863a4d021938497b440a" +dependencies = [ + "serde 1.0.192", + "thiserror", +] + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde 1.0.192", +] + +[[package]] +name = "better_any" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b359aebd937c17c725e19efcb661200883f04c49c53e7132224dac26da39d4a0" +dependencies = [ + "better_typeid_derive", +] + +[[package]] +name = "better_typeid_derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3deeecb812ca5300b7d3f66f730cc2ebd3511c3d36c691dd79c165d5b19a26e3" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde 1.0.192", +] + +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static 1.4.0", + "lazycell", + "peeking_take_while", + "prettyplease 0.2.15", + "proc-macro2 1.0.69", + "quote 1.0.33", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.38", +] + +[[package]] +name = "bip32" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30ed1d6f8437a487a266c8293aeb95b61a23261273e3e02912cdb8b68bf798b" +dependencies = [ + "bs58", + "hmac 0.12.1", + "k256", + "once_cell", + "pbkdf2 0.11.0", + "rand_core 0.6.4", + "ripemd", + "sha2 0.10.8", + "subtle", + "zeroize", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitcoin-private" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" + +[[package]] +name = "bitcoin_hashes" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501" +dependencies = [ + "bitcoin-private", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + +[[package]] +name = "bitvec" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" +dependencies = [ + "funty 1.1.0", + "radium 0.6.2", + "tap", + "wyz 0.2.0", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty 2.0.0", + "radium 0.7.0", + "tap", + "wyz 0.5.1", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +dependencies = [ + "arrayvec 0.4.12", + "constant_time_eq 0.1.5", +] + +[[package]] +name = "blake3" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +dependencies = [ + "arrayref", + "arrayvec 0.7.4", + "cc", + "cfg-if", + "constant_time_eq 0.3.0", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding 0.2.1", + "generic-array", +] + +[[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 = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blst" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94087b935a822949d3291a9989ad2b2051ea141eda0fd4e478a75f6aa3e604b" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "brotli" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +dependencies = [ + "sha2 0.9.9", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static 1.4.0", + "memchr", + "regex-automata 0.1.10", +] + +[[package]] +name = "bstr" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c" +dependencies = [ + "memchr", + "serde 1.0.192", +] + +[[package]] +name = "bulletproofs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40e698f1df446cc6246afd823afbe2d121134d089c9102c1dd26d1264991ba32" +dependencies = [ + "byteorder", + "clear_on_drop", + "curve25519-dalek-ng", + "digest 0.9.0", + "merlin", + "rand 0.8.5", + "rand_core 0.6.4", + "serde 1.0.192", + "serde_derive", + "sha3 0.9.1", + "subtle-ng", + "thiserror", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "bytecode-interpreter-crypto" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "curve25519-dalek-fiat", + "ed25519-dalek-fiat", + "sha2 0.9.9", + "sha3 0.9.1", +] + +[[package]] +name = "bytecount" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" + +[[package]] +name = "bytemuck" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +dependencies = [ + "serde 1.0.192", +] + +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + +[[package]] +name = "bytes-varint" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54c1820c7c366b9d26c47143e1604454105a59969aade54e4f695d96acc8332f" +dependencies = [ + "bytes", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "c_linked_list" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b" + +[[package]] +name = "cached" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc2fafddf188d13788e7099295a59b99e99b2148ab2195cae454e754cc099925" +dependencies = [ + "async-trait", + "async_once", + "cached_proc_macro", + "cached_proc_macro_types", + "futures", + "hashbrown 0.13.2", + "instant", + "lazy_static 1.4.0", + "once_cell", + "thiserror", + "tokio", +] + +[[package]] +name = "cached_proc_macro" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10ca87c81aaa3a949dbbe2b5e6c2c45dbc94ba4897e45ea31ff9ec5087be3dc" +dependencies = [ + "cached_proc_macro_types", + "darling 0.14.4", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "cached_proc_macro_types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" + +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" + +[[package]] +name = "captcha" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db21780337b425f968a2c3efa842eeaa4fe53d2bcb1eb27d2877460a862fb0ab" +dependencies = [ + "base64 0.13.1", + "hound", + "image", + "lodepng", + "rand 0.8.5", + "serde_json", +] + +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom 7.1.3", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_block" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18758054972164c3264f7c8386f5fc6da6114cb46b619fd365d4e3b2dc3ae487" + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits 0.2.17", + "serde 1.0.192", + "wasm-bindgen", + "windows-targets 0.48.5", +] + +[[package]] +name = "chrono-tz" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e23185c0e21df6ed832a12e2bda87c7d1def6842881fb634a8511ced741b0d76" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf", +] + +[[package]] +name = "chrono-tz-build" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" +dependencies = [ + "parse-zoneinfo", + "phf", + "phf_codegen", +] + +[[package]] +name = "chunked_transfer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "claims" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6995bbe186456c36307f8ea36be3eefe42f49d106896414e18efc4fb2f846b5" +dependencies = [ + "autocfg", +] + +[[package]] +name = "clang-sys" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim 0.8.0", + "textwrap 0.11.0", + "unicode-width", + "vec_map", +] + +[[package]] +name = "clap" +version = "4.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.10.0", + "terminal_size", +] + +[[package]] +name = "clap_complete" +version = "4.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bffe91f06a11b4b9420f62103854e90867812cd5d01557f853c5ee8e791b12ae" +dependencies = [ + "clap 4.4.10", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "clear_on_drop" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38508a63f4979f0048febc9966fadbd48e5dab31fd0ec6a3f151bbf4a74f7423" +dependencies = [ + "cc", +] + +[[package]] +name = "clipboard-win" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" +dependencies = [ + "error-code", + "str-buf", + "winapi 0.3.9", +] + +[[package]] +name = "codespan" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3362992a0d9f1dd7c3d0e89e0ab2bb540b7a95fea8cd798090e758fda2899b5e" +dependencies = [ + "codespan-reporting", + "serde 1.0.192", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "serde 1.0.192", + "termcolor", + "unicode-width", +] + +[[package]] +name = "collectable" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08abddbaad209601e53c7dd4308d8c04c06f17bb7df006434e586a22b83be45a" + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "colored" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" +dependencies = [ + "is-terminal", + "lazy_static 1.4.0", + "windows-sys 0.48.0", +] + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util 0.7.10", +] + +[[package]] +name = "config" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369" +dependencies = [ + "lazy_static 1.4.0", + "nom 5.1.3", + "rust-ini", + "serde 1.0.192", + "serde-hjson", + "serde_json", + "toml 0.5.11", + "yaml-rust", +] + +[[package]] +name = "console" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +dependencies = [ + "encode_unicode", + "lazy_static 1.4.0", + "libc", + "unicode-width", + "windows-sys 0.45.0", +] + +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + +[[package]] +name = "const-str" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aca749d3d3f5b87a0d6100509879f9cf486ab510803a4a4e1001da1ff61c2bd6" + +[[package]] +name = "const_fn" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" + +[[package]] +name = "const_format" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "unicode-xid 0.2.4", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "aes-gcm", + "base64 0.20.0", + "hkdf 0.12.3", + "hmac 0.12.1", + "percent-encoding", + "rand 0.8.5", + "sha2 0.10.8", + "subtle", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d606d0fba62e13cf04db20536c05cb7f13673c161cb47a47a82b9b9e7d3f1daa" +dependencies = [ + "cookie", + "idna 0.2.3", + "log", + "publicsuffix", + "serde 1.0.192", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32c" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f48d60e5b4d2c53d5c2b1d8a58c849a70ae5e5509b08a48d047e3b65714a74" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" +dependencies = [ + "cfg-if", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset 0.9.0", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossterm" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486d44227f71a1ef39554c0dc47e44b9f4139927c75043312690c3f476d1d788" +dependencies = [ + "bitflags 1.3.2", + "crossterm_winapi 0.8.0", + "libc", + "mio 0.7.14", + "parking_lot 0.11.2", + "signal-hook", + "signal-hook-mio", + "winapi 0.3.9", +] + +[[package]] +name = "crossterm" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85525306c4291d1b73ce93c8acf9c339f9b213aef6c1d85c3830cbf1c16325c" +dependencies = [ + "bitflags 1.3.2", + "crossterm_winapi 0.9.1", + "libc", + "mio 0.7.14", + "parking_lot 0.11.2", + "signal-hook", + "signal-hook-mio", + "winapi 0.3.9", +] + +[[package]] +name = "crossterm" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" +dependencies = [ + "bitflags 1.3.2", + "crossterm_winapi 0.9.1", + "libc", + "mio 0.8.9", + "parking_lot 0.12.1", + "signal-hook", + "signal-hook-mio", + "winapi 0.3.9", +] + +[[package]] +name = "crossterm" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13" +dependencies = [ + "bitflags 1.3.2", + "crossterm_winapi 0.9.1", + "libc", + "mio 0.8.9", + "parking_lot 0.12.1", + "signal-hook", + "signal-hook-mio", + "winapi 0.3.9", +] + +[[package]] +name = "crossterm_winapi" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6966607622438301997d3dac0d2f6e9a90c68bb6bc1785ea98456ab93c0507" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[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 0.6.4", + "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", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "csv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde 1.0.192", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-fiat" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44339b9ecede7f72a0d3b012bf9bb5a616dc8bfde23ce544e42da075c87198f0" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-ng" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "serde 1.0.192", + "subtle-ng", + "zeroize", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core 0.20.3", + "darling_macro 0.20.3", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.69", + "quote 1.0.33", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.69", + "quote 1.0.33", + "strsim 0.10.0", + "syn 2.0.38", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core 0.14.4", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core 0.20.3", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.2", + "lock_api", + "once_cell", + "parking_lot_core 0.9.9", +] + +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + +[[package]] +name = "data-encoding-macro" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" +dependencies = [ + "data-encoding", + "syn 1.0.109", +] + +[[package]] +name = "deadpool" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e" +dependencies = [ + "async-trait", + "deadpool-runtime", + "num_cpus", + "retain_mut", + "tokio", +] + +[[package]] +name = "deadpool-redis" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8bde44cbfdf17ae5baa45c9f43073b320f1a19955389315629304a23909ad2" +dependencies = [ + "deadpool", + "redis", +] + +[[package]] +name = "deadpool-runtime" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63dfa964fe2a66f3fde91fc70b267fe193d822c7e603e2a675a49a7f46ad3f49" +dependencies = [ + "tokio", +] + +[[package]] +name = "debug-ignore" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe7ed1d93f4553003e20b629abe9085e1e81b1429520f897f8f8860bc6dfc21" +dependencies = [ + "serde 1.0.192", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "pem-rfc7468 0.6.0", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "pem-rfc7468 0.7.0", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom 7.1.3", + "num-bigint", + "num-traits 0.2.17", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +dependencies = [ + "powerfmt", + "serde 1.0.192", +] + +[[package]] +name = "derivation-path" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "derive-syn-parse" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "derive_builder" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" +dependencies = [ + "darling 0.14.4", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_macro" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" +dependencies = [ + "derive_builder_core", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2 1.0.69", + "quote 1.0.33", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "deunicode" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a1abaf4d861455be59f64fd2b55606cb151fce304ede7165f410243ce96bde6" + +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[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 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "directories" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +dependencies = [ + "dirs-sys 0.3.7", +] + +[[package]] +name = "dirs" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +dependencies = [ + "dirs-sys 0.3.7", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys 0.3.7", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys 0.4.1", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "dyn-clone" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der 0.7.8", + "digest 0.10.7", + "elliptic-curve 0.13.8", + "rfc6979 0.4.0", + "signature 2.2.0", + "spki 0.7.3", +] + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "pkcs8 0.9.0", + "serde 1.0.192", + "signature 1.6.4", + "zeroize", +] + +[[package]] +name = "ed25519-consensus" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" +dependencies = [ + "curve25519-dalek-ng", + "hex", + "rand_core 0.6.4", + "serde 1.0.192", + "sha2 0.9.9", + "thiserror", + "zeroize", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand 0.7.3", + "serde 1.0.192", + "serde_bytes", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-dalek-bip32" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d2be62a4061b872c8c0873ee4fc6f101ce7b889d039f019c5fa2af471a59908" +dependencies = [ + "derivation-path", + "ed25519-dalek", + "hmac 0.12.1", + "sha2 0.10.8", +] + +[[package]] +name = "ed25519-dalek-fiat" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97c6ac152eba578c1c53d2cefe8ad02e239e3d6f971b0f1ef3cb54cd66037fa0" +dependencies = [ + "curve25519-dalek-fiat", + "ed25519", + "rand 0.8.5", + "serde 1.0.192", + "serde_bytes", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", + "generic-array", + "group 0.12.1", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.5", + "digest 0.10.7", + "ff 0.13.0", + "generic-array", + "group 0.13.0", + "pem-rfc7468 0.7.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1 0.7.3", + "subtle", + "zeroize", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "enum-compat-util" +version = "0.1.0-canonical-sui" +dependencies = [ + "serde_yaml 0.8.26", +] + +[[package]] +name = "enum_dispatch" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" +dependencies = [ + "once_cell", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "env_logger" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +dependencies = [ + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "erasable" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f11890ce181d47a64e5d1eb4b6caba0e7bae911a356723740d058a5d0340b7d" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "erased-serde" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" +dependencies = [ + "serde 1.0.192", +] + +[[package]] +name = "errno" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "error-code" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" +dependencies = [ + "libc", + "str-buf", +] + +[[package]] +name = "ethnum" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "eyre" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80f656be11ddf91bd709454d15d5bd896fbaf4cc3314e69349e4d1569f5b46cd" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fail" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be3c61c59fdc91f5dbc3ea31ee8623122ce80057058be560654c5d410d181a6" +dependencies = [ + "lazy_static 1.4.0", + "log", + "rand 0.7.3", +] + +[[package]] +name = "fail" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5e43d0f78a42ad591453aedb1d7ae631ce7ee445c7643691055a9ed8d3b01c" +dependencies = [ + "log", + "once_cell", + "rand 0.8.5", +] + +[[package]] +name = "fallible_collections" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a88c69768c0a15262df21899142bc6df9b9b823546d4b4b9a7bc2d6c448ec6fd" +dependencies = [ + "hashbrown 0.13.2", +] + +[[package]] +name = "fastcrypto" +version = "0.1.7" +source = "git+https://github.com/MystenLabs/fastcrypto?rev=517ec93ad2eba1807c9508f14953814a33523edc#517ec93ad2eba1807c9508f14953814a33523edc" +dependencies = [ + "aes", + "aes-gcm", + "ark-ec", + "ark-ff", + "ark-secp256r1", + "ark-serialize", + "auto_ops", + "base64ct", + "bincode", + "blake2", + "blake3", + "blst", + "bs58", + "bulletproofs", + "cbc", + "ctr", + "curve25519-dalek-ng", + "derive_more", + "digest 0.10.7", + "ecdsa 0.16.9", + "ed25519-consensus", + "elliptic-curve 0.13.8", + "eyre", + "fastcrypto-derive", + "generic-array", + "hex", + "hkdf 0.12.3", + "lazy_static 1.4.0", + "merlin", + "once_cell", + "p256", + "rand 0.8.5", + "readonly", + "rfc6979 0.4.0", + "rsa", + "schemars", + "secp256k1", + "serde 1.0.192", + "serde_bytes", + "serde_json", + "serde_with", + "sha2 0.10.8", + "sha3 0.10.8", + "signature 2.2.0", + "static_assertions", + "thiserror", + "tokio", + "typenum", + "zeroize", +] + +[[package]] +name = "fastcrypto-derive" +version = "0.1.3" +source = "git+https://github.com/MystenLabs/fastcrypto?rev=517ec93ad2eba1807c9508f14953814a33523edc#517ec93ad2eba1807c9508f14953814a33523edc" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "fastcrypto-tbls" +version = "0.1.0" +source = "git+https://github.com/MystenLabs/fastcrypto?rev=517ec93ad2eba1807c9508f14953814a33523edc#517ec93ad2eba1807c9508f14953814a33523edc" +dependencies = [ + "bcs 0.1.6", + "bincode", + "digest 0.10.7", + "fastcrypto", + "fastcrypto-derive", + "hex", + "itertools 0.10.5", + "rand 0.8.5", + "serde 1.0.192", + "sha3 0.10.8", + "tap", + "tracing", + "typenum", + "zeroize", +] + +[[package]] +name = "fastcrypto-zkp" +version = "0.1.2" +source = "git+https://github.com/MystenLabs/fastcrypto?rev=517ec93ad2eba1807c9508f14953814a33523edc#517ec93ad2eba1807c9508f14953814a33523edc" +dependencies = [ + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-groth16", + "ark-relations", + "ark-serialize", + "ark-snark", + "bcs 0.1.6", + "blst", + "byte-slice-cast", + "derive_more", + "fastcrypto", + "im", + "num-bigint", + "once_cell", + "poseidon-ark", + "regex", + "reqwest", + "rustls-webpki", + "schemars", + "serde 1.0.192", + "serde_json", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fd-lock" +version = "3.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" +dependencies = [ + "cfg-if", + "rustix 0.38.25", + "windows-sys 0.48.0", +] + +[[package]] +name = "fdeflate" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "fdlimit" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4c9e43643f5a3be4ca5b67d26b98031ff9db6806c3440ae32e02e3ceac3f1b" +dependencies = [ + "libc", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + +[[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.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits 0.2.17", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "gcp-bigquery-client" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ab5966c98f6d4e71e247cda6a6d8497bc8a1df3a4ba9ee548087842cffc21d" +dependencies = [ + "async-stream", + "hyper", + "hyper-rustls 0.23.2", + "log", + "reqwest", + "serde 1.0.192", + "serde_json", + "thiserror", + "time", + "tokio", + "tokio-stream", + "url", + "yup-oauth2", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "serde 1.0.192", + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "get_if_addrs" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abddb55a898d32925f3148bd281174a68eeb68bbfd9a5938a57b18f506ee4ef7" +dependencies = [ + "c_linked_list", + "get_if_addrs-sys", + "libc", + "winapi 0.2.8", +] + +[[package]] +name = "get_if_addrs-sys" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d04f9fb746cf36b191c00f3ede8bde9c8e64f9f4b05ae2694a9ccf5e3f5ab48" +dependencies = [ + "gcc", + "libc", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "gettid" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b20f40277dfb8a9dec7e2e945f6d8ff711e733c8f2a2c9b257562764b4c60d" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "git-version" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13ad01ffa8221f7fe8b936d6ffb2a3e7ad428885a04fad51866a5f33eafda57c" +dependencies = [ + "git-version-macro", +] + +[[package]] +name = "git-version-macro" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84488ccbdb24ad6f56dc1863b4a8154a7856cd3c6c7610401634fab3cb588dae" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "git2" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1" +dependencies = [ + "bitflags 1.3.2", + "libc", + "libgit2-sys", + "log", + "url", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr 1.8.0", + "log", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + +[[package]] +name = "goldenfile" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a67453a3b358bd8213aedafd4feed75eecab9fb04bed26ba6fdf94694be560" +dependencies = [ + "scopeguard", + "similar-asserts", + "tempfile", + "yansi", +] + +[[package]] +name = "governor" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "821239e5672ff23e2a7060901fa622950bbd80b649cdaadd78d1c1767ed14eb4" +dependencies = [ + "cfg-if", + "dashmap", + "futures", + "futures-timer", + "no-std-compat", + "nonzero_ext", + "parking_lot 0.12.1", + "quanta", + "rand 0.8.5", + "smallvec", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util 0.7.10", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.7", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.6", +] + +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" + +[[package]] +name = "hdrhistogram" +version = "7.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +dependencies = [ + "base64 0.21.5", + "byteorder", + "crossbeam-channel", + "flate2", + "nom 7.1.3", + "num-traits 0.2.17", +] + +[[package]] +name = "headers" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" +dependencies = [ + "base64 0.21.5", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" +dependencies = [ + "digest 0.9.0", + "hmac 0.10.1", +] + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac 0.12.1", +] + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" +dependencies = [ + "crypto-mac 0.10.1", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac 0.11.1", + "digest 0.9.0", +] + +[[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 = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac 0.8.1", +] + +[[package]] +name = "hmac-sha512" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e806677ce663d0a199541030c816847b36e8dc095f70dae4a4f4ad63da5383" + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi 0.3.9", +] + +[[package]] +name = "hound" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.10", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "log", + "rustls 0.20.9", + "rustls-native-certs", + "tokio", + "tokio-rustls 0.23.4", + "webpki-roots 0.22.6", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http", + "hyper", + "log", + "rustls 0.21.9", + "rustls-native-certs", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +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 = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747ad1b4ae841a78e8aba0d63adbfbeaea26b517b63705d47856b73015d27060" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.3", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "im" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" +dependencies = [ + "bitmaps", + "rand_core 0.6.4", + "rand_xoshiro", + "sized-chunks", + "typenum", + "version_check", +] + +[[package]] +name = "image" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-rational", + "num-traits 0.2.17", + "png", +] + +[[package]] +name = "impl-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde 1.0.192", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "include_dir" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b56e147e6187d61e9d0f039f10e070d0c0a887e24fe0bb9ca3f29bfde62cab" +dependencies = [ + "glob", + "include_dir_impl", + "proc-macro-hack", +] + +[[package]] +name = "include_dir" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +dependencies = [ + "glob", + "include_dir_macros", +] + +[[package]] +name = "include_dir_impl" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a0c890c85da4bab7bce4204c707396bbd3c6c8a681716a51c8814cfc2b682df" +dependencies = [ + "anyhow", + "proc-macro-hack", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[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", + "serde 1.0.192", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.2", +] + +[[package]] +name = "indicatif" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" +dependencies = [ + "console", + "instant", + "number_prefix", + "portable-atomic", + "unicode-width", +] + +[[package]] +name = "indoc" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" + +[[package]] +name = "inferno" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "321f0f839cd44a4686e9504b0a62b4d69a50b62072144c71c68f5873c167b8d9" +dependencies = [ + "ahash 0.8.6", + "clap 4.4.10", + "crossbeam-channel", + "crossbeam-utils", + "dashmap", + "env_logger", + "indexmap 2.1.0", + "is-terminal", + "itoa", + "log", + "num-format", + "once_cell", + "quick-xml 0.26.0", + "rgb", + "str_stack", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "block-padding 0.3.3", + "generic-array", +] + +[[package]] +name = "inquire" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33e7c1ddeb15c9abcbfef6029d8e29f69b52b6d6c891031b88ed91b5065803b" +dependencies = [ + "bitflags 1.3.2", + "crossterm 0.25.0", + "dyn-clone", + "lazy_static 1.4.0", + "newline-converter", + "thiserror", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "insta" +version = "1.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d64600be34b2fcfc267740a243fa7744441bb4947a619ac4e5bb6507f35fbfc" +dependencies = [ + "console", + "lazy_static 1.4.0", + "linked-hash-map", + "pest", + "pest_derive", + "serde 1.0.192", + "similar", + "yaml-rust", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "integer-encoding" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" + +[[package]] +name = "internment" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab388864246d58a276e60e7569a833d9cc4cd75c66e5ca77c177dad38e59996" +dependencies = [ + "ahash 0.7.7", + "dashmap", + "hashbrown 0.12.3", + "once_cell", + "parking_lot 0.12.1", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.3", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "iprange" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37209be0ad225457e63814401415e748e2453a5297f9b637338f5fb8afa4ec00" +dependencies = [ + "ipnet", +] + +[[package]] +name = "iri-string" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0f7638c1e223529f1bfdc48c8b133b9e0b434094d1d28473161ee48b235f78" +dependencies = [ + "nom 7.1.3", +] + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi 0.3.3", + "rustix 0.38.25", + "windows-sys 0.48.0", +] + +[[package]] +name = "is_debug" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06d198e9919d9822d5f7083ba8530e04de87841eaf21ead9af8f2304efd57c89" + +[[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.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "jemalloc-ctl" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cffc705424a344c054e135d12ee591402f4539245e8bbd64e6c9eaa9458b63c" +dependencies = [ + "jemalloc-sys", + "libc", + "paste", +] + +[[package]] +name = "jemalloc-sys" +version = "0.5.4+5.3.0-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac6c1946e1cea1788cbfde01c993b52a10e2da07f4bac608228d1bed20bfebf2" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "jemallocator" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0de374a9f8e63150e6f5e8a60cc14c668226d7a347d8aee1a45766e3c4dd3bc" +dependencies = [ + "jemalloc-sys", + "libc", +] + +[[package]] +name = "jobserver" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json_to_table" +version = "0.6.0" +source = "git+https://github.com/zhiburt/tabled/?rev=e449317a1c02eb6b29e409ad6617e5d9eb7b3bd4#e449317a1c02eb6b29e409ad6617e5d9eb7b3bd4" +dependencies = [ + "serde_json", + "tabled", +] + +[[package]] +name = "jsonrpsee" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-proc-macros", + "jsonrpsee-server", + "jsonrpsee-types", + "jsonrpsee-ws-client", + "tracing", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "futures-util", + "http", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project", + "rustls-native-certs", + "soketto", + "thiserror", + "tokio", + "tokio-rustls 0.23.4", + "tokio-util 0.7.10", + "tracing", + "webpki-roots 0.22.6", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "anyhow", + "arrayvec 0.7.4", + "async-lock", + "async-trait", + "beef", + "futures-channel", + "futures-timer", + "futures-util", + "globset", + "hyper", + "jsonrpsee-types", + "parking_lot 0.12.1", + "rand 0.8.5", + "rustc-hash", + "serde 1.0.192", + "serde_json", + "soketto", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "async-trait", + "hyper", + "hyper-rustls 0.23.2", + "jsonrpsee-core", + "jsonrpsee-types", + "rustc-hash", + "serde 1.0.192", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 1.1.3", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "futures-channel", + "futures-util", + "http", + "hyper", + "jsonrpsee-core", + "jsonrpsee-types", + "serde 1.0.192", + "serde_json", + "soketto", + "tokio", + "tokio-stream", + "tokio-util 0.7.10", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "anyhow", + "beef", + "serde 1.0.192", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "http", + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", +] + +[[package]] +name = "jsonwebtoken" +version = "8.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" +dependencies = [ + "base64 0.21.5", + "pem", + "ring 0.16.20", + "serde 1.0.192", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.8", + "sha3 0.10.8", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "lexical-core" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +dependencies = [ + "arrayvec 0.5.2", + "bitflags 1.3.2", + "cfg-if", + "ryu", + "static_assertions", +] + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "libgit2-sys" +version = "0.14.2+1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" +dependencies = [ + "cc", + "libc", + "libz-sys", + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi 0.3.9", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall 0.4.1", +] + +[[package]] +name = "librocksdb-sys" +version = "0.11.0+8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" +dependencies = [ + "bindgen", + "bzip2-sys", + "cc", + "glob", + "libc", + "libz-sys", + "lz4-sys", + "zstd-sys", +] + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde 1.0.192", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libz-sys" +version = "1.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "linux-raw-sys" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "lodepng" +version = "3.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f56ff9bcd5721ab172b73eac8a7d4e9439f47a98581e666178dbe7df97e13" +dependencies = [ + "crc32fast", + "fallible_collections", + "flate2", + "libc", + "rgb", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +dependencies = [ + "serde 1.0.192", +] + +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown 0.12.3", +] + +[[package]] +name = "lru" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e7d46de488603ffdd5f30afbc64fbba2378214a2c3a2fb83abf3d33126df17" +dependencies = [ + "hashbrown 0.13.2", +] + +[[package]] +name = "lru" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" +dependencies = [ + "hashbrown 0.13.2", +] + +[[package]] +name = "lz4" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" +dependencies = [ + "libc", + "lz4-sys", +] + +[[package]] +name = "lz4-sys" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "mach2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" +dependencies = [ + "libc", +] + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "match_opt" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "405ba1524a1e6ae755334d6966380c60ec40157e0155f9032dd3c294b6384da9" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matchit" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "md-5" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest 0.10.7", +] + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "memmap2" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minibytes" +version = "0.1.0" +source = "git+https://github.com/MystenLabs/mysticeti?rev=318d61d27f47d257d99a86983d835e9e9756bc59#318d61d27f47d257d99a86983d835e9e9756bc59" +dependencies = [ + "memmap2", + "serde 1.0.192", +] + +[[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.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow", + "ntapi 0.3.7", + "winapi 0.3.9", +] + +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "mirai-annotations" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" + +[[package]] +name = "mockall" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "lazy_static 1.4.0", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" +dependencies = [ + "cfg-if", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "more-asserts" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" + +[[package]] +name = "move-abigen" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "bcs 0.1.4", + "heck 0.3.3", + "log", + "move-binary-format 0.0.3-canonical-aptos", + "move-bytecode-verifier 0.1.0-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-model 0.1.0-canonical-aptos", + "serde 1.0.192", +] + +[[package]] +name = "move-abigen" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "bcs 0.1.6", + "heck 0.3.3", + "log", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-verifier 0.1.0-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-model 0.1.0-canonical-sui", + "serde 1.0.192", +] + +[[package]] +name = "move-abstract-stack" +version = "0.0.1-canonical-sui" + +[[package]] +name = "move-binary-format" +version = "0.0.3-canonical-aptos" +dependencies = [ + "anyhow", + "arbitrary", + "backtrace", + "indexmap 1.9.3", + "move-core-types 0.0.4-canonical-aptos", + "once_cell", + "proptest", + "proptest-derive", + "ref-cast", + "serde 1.0.192", + "variant_count", +] + +[[package]] +name = "move-binary-format" +version = "0.0.3-canonical-sui" +dependencies = [ + "anyhow", + "enum-compat-util", + "move-core-types 0.0.4-canonical-sui", + "move-proc-macros", + "ref-cast", + "serde 1.0.192", + "variant_count", +] + +[[package]] +name = "move-borrow-graph" +version = "0.0.1-canonical-aptos" + +[[package]] +name = "move-borrow-graph" +version = "0.0.1-canonical-sui" + +[[package]] +name = "move-bytecode-source-map" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "bcs 0.1.4", + "move-binary-format 0.0.3-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-ir-types 0.1.0-canonical-aptos", + "move-symbol-pool 0.1.0-canonical-aptos", + "serde 1.0.192", +] + +[[package]] +name = "move-bytecode-source-map" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "bcs 0.1.6", + "move-binary-format 0.0.3-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-ir-types 0.1.0-canonical-sui", + "move-symbol-pool 0.1.0-canonical-sui", + "serde 1.0.192", +] + +[[package]] +name = "move-bytecode-utils" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "move-binary-format 0.0.3-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "petgraph 0.5.1", + "serde-reflection 0.3.6", +] + +[[package]] +name = "move-bytecode-utils" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "move-binary-format 0.0.3-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "petgraph 0.5.1", + "serde-reflection 0.3.6", +] + +[[package]] +name = "move-bytecode-verifier" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "fail 0.4.0", + "move-binary-format 0.0.3-canonical-aptos", + "move-borrow-graph 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "petgraph 0.5.1", + "typed-arena", +] + +[[package]] +name = "move-bytecode-verifier" +version = "0.1.0-canonical-sui" +dependencies = [ + "move-abstract-stack", + "move-binary-format 0.0.3-canonical-sui", + "move-borrow-graph 0.0.1-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-vm-config", + "petgraph 0.5.1", +] + +[[package]] +name = "move-bytecode-verifier-v0" +version = "0.1.0-canonical-sui" +dependencies = [ + "move-abstract-stack", + "move-binary-format 0.0.3-canonical-sui", + "move-borrow-graph 0.0.1-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-vm-config", + "petgraph 0.5.1", +] + +[[package]] +name = "move-bytecode-verifier-v1" +version = "0.1.0-canonical-sui" +dependencies = [ + "move-abstract-stack", + "move-binary-format 0.0.3-canonical-sui", + "move-borrow-graph 0.0.1-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-vm-config", + "petgraph 0.5.1", +] + +[[package]] +name = "move-bytecode-viewer" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "clap 4.4.10", + "crossterm 0.26.1", + "move-binary-format 0.0.3-canonical-aptos", + "move-bytecode-source-map 0.1.0-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-disassembler 0.1.0-canonical-aptos", + "move-ir-types 0.1.0-canonical-aptos", + "regex", + "tui 0.19.0", +] + +[[package]] +name = "move-bytecode-viewer" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "clap 4.4.10", + "crossterm 0.21.0", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-source-map 0.1.0-canonical-sui", + "move-disassembler 0.1.0-canonical-sui", + "regex", + "tui 0.17.0", +] + +[[package]] +name = "move-cli" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "bcs 0.1.4", + "clap 4.4.10", + "codespan-reporting", + "colored", + "difference", + "itertools 0.10.5", + "move-binary-format 0.0.3-canonical-aptos", + "move-bytecode-source-map 0.1.0-canonical-aptos", + "move-bytecode-utils 0.1.0-canonical-aptos", + "move-bytecode-verifier 0.1.0-canonical-aptos", + "move-bytecode-viewer 0.1.0-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-compiler 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-coverage 0.1.0-canonical-aptos", + "move-disassembler 0.1.0-canonical-aptos", + "move-docgen 0.1.0-canonical-aptos", + "move-errmapgen 0.1.0-canonical-aptos", + "move-ir-types 0.1.0-canonical-aptos", + "move-package 0.1.0-canonical-aptos", + "move-prover 0.1.0-canonical-aptos", + "move-resource-viewer", + "move-stdlib 0.1.1-canonical-aptos", + "move-symbol-pool 0.1.0-canonical-aptos", + "move-unit-test 0.1.0-canonical-aptos", + "move-vm-runtime 0.1.0-canonical-aptos", + "move-vm-test-utils 0.1.0-canonical-aptos", + "move-vm-types 0.1.0-canonical-aptos", + "once_cell", + "reqwest", + "serde 1.0.192", + "serde_json", + "serde_yaml 0.8.26", + "tempfile", + "toml_edit 0.14.4", + "walkdir", +] + +[[package]] +name = "move-cli" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "bcs 0.1.6", + "clap 4.4.10", + "codespan-reporting", + "colored", + "difference", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-bytecode-verifier 0.1.0-canonical-sui", + "move-bytecode-viewer 0.1.0-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-compiler 0.0.1-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-coverage 0.1.0-canonical-sui", + "move-disassembler 0.1.0-canonical-sui", + "move-docgen 0.1.0-canonical-sui", + "move-errmapgen 0.1.0-canonical-sui", + "move-ir-types 0.1.0-canonical-sui", + "move-package 0.1.0-canonical-sui", + "move-prover 0.1.0-canonical-sui", + "move-read-write-set-types", + "move-stdlib 0.1.1-canonical-sui", + "move-unit-test 0.1.0-canonical-sui", + "move-vm-profiler", + "move-vm-runtime 0.1.0-canonical-sui", + "move-vm-test-utils 0.1.0-canonical-sui", + "move-vm-types 0.1.0-canonical-sui", + "serde_yaml 0.8.26", + "tempfile", + "toml_edit 0.14.4", + "walkdir", +] + +[[package]] +name = "move-command-line-common" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "difference", + "dirs-next", + "hex", + "move-core-types 0.0.4-canonical-aptos", + "num-bigint", + "once_cell", + "serde 1.0.192", + "sha2 0.9.9", + "walkdir", +] + +[[package]] +name = "move-command-line-common" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "difference", + "dirs-next", + "hex", + "move-core-types 0.0.4-canonical-sui", + "num-bigint", + "once_cell", + "serde 1.0.192", + "sha2 0.9.9", + "walkdir", +] + +[[package]] +name = "move-compiler" +version = "0.0.1-canonical-aptos" +dependencies = [ + "anyhow", + "bcs 0.1.4", + "clap 4.4.10", + "codespan-reporting", + "difference", + "hex", + "move-binary-format 0.0.3-canonical-aptos", + "move-borrow-graph 0.0.1-canonical-aptos", + "move-bytecode-source-map 0.1.0-canonical-aptos", + "move-bytecode-verifier 0.1.0-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-ir-to-bytecode 0.1.0-canonical-aptos", + "move-ir-types 0.1.0-canonical-aptos", + "move-symbol-pool 0.1.0-canonical-aptos", + "num-bigint", + "once_cell", + "petgraph 0.5.1", + "regex", + "sha3 0.9.1", + "tempfile", + "walkdir", +] + +[[package]] +name = "move-compiler" +version = "0.0.1-canonical-sui" +dependencies = [ + "anyhow", + "bcs 0.1.6", + "clap 4.4.10", + "codespan-reporting", + "hex", + "move-binary-format 0.0.3-canonical-sui", + "move-borrow-graph 0.0.1-canonical-sui", + "move-bytecode-source-map 0.1.0-canonical-sui", + "move-bytecode-verifier 0.1.0-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-ir-to-bytecode 0.1.0-canonical-sui", + "move-ir-types 0.1.0-canonical-sui", + "move-symbol-pool 0.1.0-canonical-sui", + "once_cell", + "petgraph 0.5.1", + "regex", + "serde 1.0.192", + "tempfile", +] + +[[package]] +name = "move-core-types" +version = "0.0.4-canonical-aptos" +dependencies = [ + "anyhow", + "arbitrary", + "bcs 0.1.4", + "ethnum", + "hex", + "num", + "once_cell", + "primitive-types", + "proptest", + "proptest-derive", + "rand 0.8.5", + "ref-cast", + "serde 1.0.192", + "serde_bytes", + "uint", +] + +[[package]] +name = "move-core-types" +version = "0.0.4-canonical-sui" +dependencies = [ + "anyhow", + "bcs 0.1.6", + "enum-compat-util", + "ethnum", + "hex", + "move-proc-macros", + "num", + "once_cell", + "primitive-types", + "rand 0.8.5", + "ref-cast", + "serde 1.0.192", + "serde_bytes", + "uint", +] + +[[package]] +name = "move-coverage" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "bcs 0.1.4", + "clap 4.4.10", + "codespan", + "colored", + "move-binary-format 0.0.3-canonical-aptos", + "move-bytecode-source-map 0.1.0-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-ir-types 0.1.0-canonical-aptos", + "once_cell", + "petgraph 0.5.1", + "serde 1.0.192", +] + +[[package]] +name = "move-coverage" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "bcs 0.1.6", + "clap 4.4.10", + "codespan", + "colored", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-source-map 0.1.0-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-ir-types 0.1.0-canonical-sui", + "petgraph 0.5.1", + "serde 1.0.192", +] + +[[package]] +name = "move-disassembler" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "clap 4.4.10", + "colored", + "move-binary-format 0.0.3-canonical-aptos", + "move-bytecode-source-map 0.1.0-canonical-aptos", + "move-bytecode-verifier 0.1.0-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-compiler 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-coverage 0.1.0-canonical-aptos", + "move-ir-types 0.1.0-canonical-aptos", +] + +[[package]] +name = "move-disassembler" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "bcs 0.1.6", + "clap 4.4.10", + "colored", + "hex", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-source-map 0.1.0-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-compiler 0.0.1-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-coverage 0.1.0-canonical-sui", + "move-ir-types 0.1.0-canonical-sui", +] + +[[package]] +name = "move-docgen" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "codespan", + "codespan-reporting", + "itertools 0.10.5", + "log", + "move-compiler 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-model 0.1.0-canonical-aptos", + "num", + "once_cell", + "regex", + "serde 1.0.192", +] + +[[package]] +name = "move-docgen" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "codespan", + "codespan-reporting", + "itertools 0.10.5", + "log", + "move-compiler 0.0.1-canonical-sui", + "move-model 0.1.0-canonical-sui", + "num", + "once_cell", + "regex", + "serde 1.0.192", +] + +[[package]] +name = "move-errmapgen" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "bcs 0.1.4", + "log", + "move-command-line-common 0.1.0-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-model 0.1.0-canonical-aptos", + "serde 1.0.192", +] + +[[package]] +name = "move-errmapgen" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "move-command-line-common 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-model 0.1.0-canonical-sui", + "serde 1.0.192", +] + +[[package]] +name = "move-ir-compiler" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "bcs 0.1.4", + "clap 4.4.10", + "move-binary-format 0.0.3-canonical-aptos", + "move-bytecode-source-map 0.1.0-canonical-aptos", + "move-bytecode-verifier 0.1.0-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-ir-to-bytecode 0.1.0-canonical-aptos", + "move-ir-types 0.1.0-canonical-aptos", + "move-symbol-pool 0.1.0-canonical-aptos", + "serde_json", +] + +[[package]] +name = "move-ir-to-bytecode" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "codespan-reporting", + "log", + "move-binary-format 0.0.3-canonical-aptos", + "move-bytecode-source-map 0.1.0-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-ir-to-bytecode-syntax 0.1.0-canonical-aptos", + "move-ir-types 0.1.0-canonical-aptos", + "move-symbol-pool 0.1.0-canonical-aptos", + "ouroboros 0.9.5", + "thiserror", +] + +[[package]] +name = "move-ir-to-bytecode" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "codespan-reporting", + "log", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-source-map 0.1.0-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-ir-to-bytecode-syntax 0.1.0-canonical-sui", + "move-ir-types 0.1.0-canonical-sui", + "move-symbol-pool 0.1.0-canonical-sui", + "ouroboros 0.17.2", +] + +[[package]] +name = "move-ir-to-bytecode-syntax" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "hex", + "move-command-line-common 0.1.0-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-ir-types 0.1.0-canonical-aptos", + "move-symbol-pool 0.1.0-canonical-aptos", +] + +[[package]] +name = "move-ir-to-bytecode-syntax" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "hex", + "move-command-line-common 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-ir-types 0.1.0-canonical-sui", + "move-symbol-pool 0.1.0-canonical-sui", +] + +[[package]] +name = "move-ir-types" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "hex", + "move-command-line-common 0.1.0-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-symbol-pool 0.1.0-canonical-aptos", + "once_cell", + "serde 1.0.192", +] + +[[package]] +name = "move-ir-types" +version = "0.1.0-canonical-sui" +dependencies = [ + "hex", + "move-command-line-common 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-symbol-pool 0.1.0-canonical-sui", + "once_cell", + "serde 1.0.192", +] + +[[package]] +name = "move-model" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "codespan", + "codespan-reporting", + "internment", + "itertools 0.10.5", + "log", + "move-binary-format 0.0.3-canonical-aptos", + "move-bytecode-source-map 0.1.0-canonical-aptos", + "move-bytecode-verifier 0.1.0-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-compiler 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-disassembler 0.1.0-canonical-aptos", + "move-ir-types 0.1.0-canonical-aptos", + "move-symbol-pool 0.1.0-canonical-aptos", + "num", + "once_cell", + "regex", + "serde 1.0.192", + "trace", +] + +[[package]] +name = "move-model" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "codespan", + "codespan-reporting", + "internment", + "itertools 0.10.5", + "log", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-source-map 0.1.0-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-compiler 0.0.1-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-disassembler 0.1.0-canonical-sui", + "move-ir-types 0.1.0-canonical-sui", + "move-symbol-pool 0.1.0-canonical-sui", + "num", + "once_cell", + "regex", + "serde 1.0.192", +] + +[[package]] +name = "move-package" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "bcs 0.1.4", + "clap 4.4.10", + "colored", + "dirs-next", + "itertools 0.10.5", + "move-abigen 0.1.0-canonical-aptos", + "move-binary-format 0.0.3-canonical-aptos", + "move-bytecode-source-map 0.1.0-canonical-aptos", + "move-bytecode-utils 0.1.0-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-compiler 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-docgen 0.1.0-canonical-aptos", + "move-model 0.1.0-canonical-aptos", + "move-symbol-pool 0.1.0-canonical-aptos", + "named-lock", + "once_cell", + "petgraph 0.5.1", + "ptree", + "regex", + "reqwest", + "serde 1.0.192", + "serde_yaml 0.8.26", + "sha2 0.9.9", + "tempfile", + "toml 0.5.11", + "walkdir", + "whoami", +] + +[[package]] +name = "move-package" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "clap 4.4.10", + "colored", + "move-abigen 0.1.0-canonical-sui", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-source-map 0.1.0-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-compiler 0.0.1-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-docgen 0.1.0-canonical-sui", + "move-model 0.1.0-canonical-sui", + "move-symbol-pool 0.1.0-canonical-sui", + "named-lock", + "once_cell", + "petgraph 0.5.1", + "regex", + "serde 1.0.192", + "serde_yaml 0.8.26", + "sha2 0.9.9", + "tempfile", + "toml 0.5.11", + "treeline", + "walkdir", + "whoami", +] + +[[package]] +name = "move-proc-macros" +version = "0.1.0-canonical-sui" +dependencies = [ + "enum-compat-util", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "move-prover" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "async-trait", + "atty", + "clap 4.4.10", + "codespan", + "codespan-reporting", + "futures", + "hex", + "itertools 0.10.5", + "log", + "move-abigen 0.1.0-canonical-aptos", + "move-binary-format 0.0.3-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-compiler 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-docgen 0.1.0-canonical-aptos", + "move-errmapgen 0.1.0-canonical-aptos", + "move-ir-types 0.1.0-canonical-aptos", + "move-model 0.1.0-canonical-aptos", + "move-prover-boogie-backend 0.1.0-canonical-aptos", + "move-stackless-bytecode 0.1.0-canonical-aptos", + "num", + "once_cell", + "pretty", + "rand 0.8.5", + "serde 1.0.192", + "serde_json", + "simplelog", + "tokio", + "toml 0.5.11", +] + +[[package]] +name = "move-prover" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "clap 4.4.10", + "codespan-reporting", + "itertools 0.10.5", + "log", + "move-abigen 0.1.0-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-compiler 0.0.1-canonical-sui", + "move-docgen 0.1.0-canonical-sui", + "move-errmapgen 0.1.0-canonical-sui", + "move-model 0.1.0-canonical-sui", + "move-prover-boogie-backend 0.1.0-canonical-sui", + "move-stackless-bytecode 0.1.0-canonical-sui", + "once_cell", + "serde 1.0.192", + "simplelog", + "toml 0.5.11", +] + +[[package]] +name = "move-prover-boogie-backend" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "async-trait", + "codespan", + "codespan-reporting", + "futures", + "itertools 0.10.5", + "log", + "move-binary-format 0.0.3-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-compiler 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-model 0.1.0-canonical-aptos", + "move-stackless-bytecode 0.1.0-canonical-aptos", + "num", + "once_cell", + "pretty", + "rand 0.8.5", + "regex", + "serde 1.0.192", + "serde_json", + "tera", + "tokio", +] + +[[package]] +name = "move-prover-boogie-backend" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "async-trait", + "codespan", + "codespan-reporting", + "futures", + "itertools 0.10.5", + "log", + "move-binary-format 0.0.3-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-model 0.1.0-canonical-sui", + "move-stackless-bytecode 0.1.0-canonical-sui", + "num", + "once_cell", + "pretty", + "rand 0.8.5", + "regex", + "serde 1.0.192", + "tera", + "tokio", +] + +[[package]] +name = "move-read-write-set-types" +version = "0.0.3-canonical-sui" +dependencies = [ + "move-binary-format 0.0.3-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "serde 1.0.192", +] + +[[package]] +name = "move-resource-viewer" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "bcs 0.1.4", + "hex", + "move-binary-format 0.0.3-canonical-aptos", + "move-bytecode-utils 0.1.0-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "once_cell", + "serde 1.0.192", +] + +[[package]] +name = "move-stackless-bytecode" +version = "0.1.0-canonical-aptos" +dependencies = [ + "codespan", + "codespan-reporting", + "ethnum", + "im", + "itertools 0.10.5", + "log", + "move-binary-format 0.0.3-canonical-aptos", + "move-borrow-graph 0.0.1-canonical-aptos", + "move-bytecode-verifier 0.1.0-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-compiler 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-ir-to-bytecode 0.1.0-canonical-aptos", + "move-model 0.1.0-canonical-aptos", + "num", + "once_cell", + "paste", + "petgraph 0.5.1", + "serde 1.0.192", +] + +[[package]] +name = "move-stackless-bytecode" +version = "0.1.0-canonical-sui" +dependencies = [ + "codespan", + "codespan-reporting", + "ethnum", + "im", + "itertools 0.10.5", + "log", + "move-binary-format 0.0.3-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-compiler 0.0.1-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-model 0.1.0-canonical-sui", + "move-read-write-set-types", + "num", + "paste", + "petgraph 0.5.1", + "serde 1.0.192", +] + +[[package]] +name = "move-stackless-bytecode-interpreter" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "bytecode-interpreter-crypto", + "clap 4.4.10", + "codespan-reporting", + "itertools 0.10.5", + "move-binary-format 0.0.3-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-model 0.1.0-canonical-sui", + "move-stackless-bytecode 0.1.0-canonical-sui", + "num", +] + +[[package]] +name = "move-stdlib" +version = "0.1.1-canonical-aptos" +dependencies = [ + "anyhow", + "hex", + "log", + "move-binary-format 0.0.3-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-compiler 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-docgen 0.1.0-canonical-aptos", + "move-errmapgen 0.1.0-canonical-aptos", + "move-prover 0.1.0-canonical-aptos", + "move-vm-runtime 0.1.0-canonical-aptos", + "move-vm-types 0.1.0-canonical-aptos", + "sha2 0.9.9", + "sha3 0.9.1", + "smallvec", + "walkdir", +] + +[[package]] +name = "move-stdlib" +version = "0.1.1-canonical-sui" +dependencies = [ + "anyhow", + "hex", + "log", + "move-binary-format 0.0.3-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-docgen 0.1.0-canonical-sui", + "move-errmapgen 0.1.0-canonical-sui", + "move-prover 0.1.0-canonical-sui", + "move-vm-runtime 0.1.0-canonical-sui", + "move-vm-types 0.1.0-canonical-sui", + "sha2 0.9.9", + "sha3 0.9.1", + "smallvec", + "walkdir", +] + +[[package]] +name = "move-stdlib-v0" +version = "0.1.1-canonical-sui" +dependencies = [ + "anyhow", + "hex", + "log", + "move-binary-format 0.0.3-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-docgen 0.1.0-canonical-sui", + "move-errmapgen 0.1.0-canonical-sui", + "move-prover 0.1.0-canonical-sui", + "move-vm-runtime-v0", + "move-vm-types 0.1.0-canonical-sui", + "sha2 0.9.9", + "sha3 0.9.1", + "smallvec", + "walkdir", +] + +[[package]] +name = "move-stdlib-v1" +version = "0.1.1-canonical-sui" +dependencies = [ + "anyhow", + "hex", + "log", + "move-binary-format 0.0.3-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-docgen 0.1.0-canonical-sui", + "move-errmapgen 0.1.0-canonical-sui", + "move-prover 0.1.0-canonical-sui", + "move-vm-runtime-v1", + "move-vm-types 0.1.0-canonical-sui", + "sha2 0.9.9", + "sha3 0.9.1", + "smallvec", + "walkdir", +] + +[[package]] +name = "move-symbol-pool" +version = "0.1.0-canonical-aptos" +dependencies = [ + "once_cell", + "serde 1.0.192", +] + +[[package]] +name = "move-symbol-pool" +version = "0.1.0-canonical-sui" +dependencies = [ + "once_cell", + "phf", + "serde 1.0.192", +] + +[[package]] +name = "move-table-extension" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "bcs 0.1.4", + "better_any", + "move-binary-format 0.0.3-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-vm-runtime 0.1.0-canonical-aptos", + "move-vm-types 0.1.0-canonical-aptos", + "once_cell", + "sha3 0.9.1", + "smallvec", +] + +[[package]] +name = "move-transactional-test-runner" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "clap 4.4.10", + "colored", + "move-binary-format 0.0.3-canonical-aptos", + "move-bytecode-source-map 0.1.0-canonical-aptos", + "move-bytecode-utils 0.1.0-canonical-aptos", + "move-bytecode-verifier 0.1.0-canonical-aptos", + "move-cli 0.1.0-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-compiler 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-disassembler 0.1.0-canonical-aptos", + "move-ir-compiler", + "move-ir-types 0.1.0-canonical-aptos", + "move-resource-viewer", + "move-stdlib 0.1.1-canonical-aptos", + "move-symbol-pool 0.1.0-canonical-aptos", + "move-vm-runtime 0.1.0-canonical-aptos", + "move-vm-test-utils 0.1.0-canonical-aptos", + "move-vm-types 0.1.0-canonical-aptos", + "once_cell", + "rayon", + "regex", + "tempfile", +] + +[[package]] +name = "move-unit-test" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "better_any", + "clap 4.4.10", + "codespan-reporting", + "colored", + "itertools 0.10.5", + "move-binary-format 0.0.3-canonical-aptos", + "move-bytecode-utils 0.1.0-canonical-aptos", + "move-command-line-common 0.1.0-canonical-aptos", + "move-compiler 0.0.1-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-ir-types 0.1.0-canonical-aptos", + "move-model 0.1.0-canonical-aptos", + "move-resource-viewer", + "move-stdlib 0.1.1-canonical-aptos", + "move-symbol-pool 0.1.0-canonical-aptos", + "move-table-extension", + "move-vm-runtime 0.1.0-canonical-aptos", + "move-vm-test-utils 0.1.0-canonical-aptos", + "move-vm-types 0.1.0-canonical-aptos", + "once_cell", + "rayon", + "regex", +] + +[[package]] +name = "move-unit-test" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "better_any", + "clap 4.4.10", + "codespan-reporting", + "colored", + "itertools 0.10.5", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-compiler 0.0.1-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-ir-types 0.1.0-canonical-sui", + "move-model 0.1.0-canonical-sui", + "move-stackless-bytecode-interpreter", + "move-stdlib 0.1.1-canonical-sui", + "move-symbol-pool 0.1.0-canonical-sui", + "move-vm-profiler", + "move-vm-runtime 0.1.0-canonical-sui", + "move-vm-test-utils 0.1.0-canonical-sui", + "move-vm-types 0.1.0-canonical-sui", + "once_cell", + "rayon", + "regex", +] + +[[package]] +name = "move-vm-config" +version = "0.1.0-canonical-sui" +dependencies = [ + "move-binary-format 0.0.3-canonical-sui", +] + +[[package]] +name = "move-vm-profiler" +version = "0.1.0-canonical-sui" +dependencies = [ + "move-vm-config", + "once_cell", + "serde 1.0.192", + "serde_json", +] + +[[package]] +name = "move-vm-runtime" +version = "0.1.0-canonical-aptos" +dependencies = [ + "better_any", + "fail 0.4.0", + "move-binary-format 0.0.3-canonical-aptos", + "move-bytecode-verifier 0.1.0-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-vm-types 0.1.0-canonical-aptos", + "once_cell", + "parking_lot 0.11.2", + "sha3 0.9.1", + "tracing", +] + +[[package]] +name = "move-vm-runtime" +version = "0.1.0-canonical-sui" +dependencies = [ + "better_any", + "fail 0.4.0", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-verifier 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-vm-config", + "move-vm-profiler", + "move-vm-types 0.1.0-canonical-sui", + "once_cell", + "parking_lot 0.11.2", + "sha3 0.9.1", + "smallvec", + "tracing", +] + +[[package]] +name = "move-vm-runtime-v0" +version = "0.1.0-canonical-sui" +dependencies = [ + "better_any", + "fail 0.4.0", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-verifier-v0", + "move-core-types 0.0.4-canonical-sui", + "move-vm-config", + "move-vm-profiler", + "move-vm-types 0.1.0-canonical-sui", + "once_cell", + "parking_lot 0.11.2", + "sha3 0.9.1", + "smallvec", + "tracing", +] + +[[package]] +name = "move-vm-runtime-v1" +version = "0.1.0-canonical-sui" +dependencies = [ + "better_any", + "fail 0.4.0", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-verifier-v1", + "move-core-types 0.0.4-canonical-sui", + "move-vm-config", + "move-vm-profiler", + "move-vm-types 0.1.0-canonical-sui", + "once_cell", + "parking_lot 0.11.2", + "sha3 0.9.1", + "smallvec", + "tracing", +] + +[[package]] +name = "move-vm-test-utils" +version = "0.1.0-canonical-aptos" +dependencies = [ + "anyhow", + "move-binary-format 0.0.3-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "move-table-extension", + "move-vm-types 0.1.0-canonical-aptos", + "once_cell", + "serde 1.0.192", +] + +[[package]] +name = "move-vm-test-utils" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "move-binary-format 0.0.3-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-vm-profiler", + "move-vm-types 0.1.0-canonical-sui", + "once_cell", + "serde 1.0.192", +] + +[[package]] +name = "move-vm-types" +version = "0.1.0-canonical-aptos" +dependencies = [ + "bcs 0.1.4", + "move-binary-format 0.0.3-canonical-aptos", + "move-core-types 0.0.4-canonical-aptos", + "once_cell", + "serde 1.0.192", + "smallvec", +] + +[[package]] +name = "move-vm-types" +version = "0.1.0-canonical-sui" +dependencies = [ + "bcs 0.1.6", + "move-binary-format 0.0.3-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-vm-profiler", + "serde 1.0.192", + "smallvec", +] + +[[package]] +name = "movement" +version = "0.1.0" +dependencies = [ + "anyhow", + "aptos", + "async-trait", + "clap 4.4.10", + "const-str", + "dirs 3.0.2", + "reqwest", + "semver", + "serde 1.0.192", + "serde_json", + "sui", + "tempfile", + "tokio", +] + +[[package]] +name = "movement-sdk" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "serde 1.0.192", +] + +[[package]] +name = "msim" +version = "0.1.0" +source = "git+https://github.com/MystenLabs/mysten-sim.git?rev=1a52783d6600ecc22e15253a982f77881bd47c77#1a52783d6600ecc22e15253a982f77881bd47c77" +dependencies = [ + "ahash 0.7.7", + "async-task", + "bincode", + "bytes", + "cc", + "downcast-rs", + "erasable", + "futures", + "lazy_static 1.4.0", + "libc", + "msim-macros", + "naive-timer", + "pin-project-lite", + "rand 0.8.5", + "real_tokio", + "serde 1.0.192", + "socket2 0.4.10", + "tap", + "tokio-util 0.7.7", + "toml 0.5.11", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "msim-macros" +version = "0.1.0" +source = "git+https://github.com/MystenLabs/mysten-sim.git?rev=1a52783d6600ecc22e15253a982f77881bd47c77#1a52783d6600ecc22e15253a982f77881bd47c77" +dependencies = [ + "darling 0.14.4", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "multer" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http", + "httparse", + "log", + "memchr", + "mime", + "spin 0.9.8", + "tokio", + "version_check", +] + +[[package]] +name = "multiaddr" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b36f567c7099511fa8612bbbb52dda2419ce0bdbacf31714e3a5ffdb766d3bd" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "log", + "multibase", + "multihash", + "percent-encoding", + "serde 1.0.192", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multibase" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" +dependencies = [ + "core2", + "multihash-derive", + "unsigned-varint", +] + +[[package]] +name = "multihash-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" +dependencies = [ + "proc-macro-crate 1.1.3", + "proc-macro-error", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "mysten-common" +version = "0.1.0-canonical-sui" +dependencies = [ + "futures", + "parking_lot 0.12.1", + "tokio", + "workspace-hack", +] + +[[package]] +name = "mysten-metrics" +version = "0.7.0-canonical-sui" +dependencies = [ + "async-trait", + "axum", + "dashmap", + "futures", + "once_cell", + "parking_lot 0.12.1", + "prometheus", + "prometheus-closure-metric", + "scopeguard", + "tap", + "tokio", + "tracing", + "uuid", + "workspace-hack", +] + +[[package]] +name = "mysten-network" +version = "0.2.0-canonical-sui" +dependencies = [ + "anemo", + "bcs 0.1.6", + "bytes", + "eyre", + "futures", + "http", + "multiaddr", + "serde 1.0.192", + "snap", + "tokio", + "tokio-stream", + "tonic 0.10.2", + "tonic-health", + "tower", + "tower-http", + "tracing", + "workspace-hack", +] + +[[package]] +name = "mysten-util-mem" +version = "0.11.0-canonical-sui" +dependencies = [ + "cfg-if", + "ed25519-consensus", + "fastcrypto", + "fastcrypto-tbls", + "hashbrown 0.12.3", + "impl-trait-for-tuples", + "indexmap 1.9.3", + "mysten-util-mem-derive", + "once_cell", + "parking_lot 0.12.1", + "roaring", + "smallvec", + "workspace-hack", +] + +[[package]] +name = "mysten-util-mem-derive" +version = "0.1.0-canonical-sui" +dependencies = [ + "proc-macro2 1.0.69", + "syn 1.0.109", + "synstructure", + "workspace-hack", +] + +[[package]] +name = "mysticeti-core" +version = "0.1.0" +source = "git+https://github.com/MystenLabs/mysticeti?rev=318d61d27f47d257d99a86983d835e9e9756bc59#318d61d27f47d257d99a86983d835e9e9756bc59" +dependencies = [ + "async-trait", + "axum", + "bincode", + "blake2", + "crc32fast", + "digest 0.10.7", + "ed25519-consensus", + "eyre", + "futures", + "gettid", + "hex", + "hyper", + "libc", + "memmap2", + "minibytes", + "parking_lot 0.12.1", + "prometheus", + "rand 0.8.5", + "serde 1.0.192", + "serde_yaml 0.9.27", + "tabled", + "tempfile", + "tokio", + "tracing", + "tracing-core", + "tracing-subscriber", + "zeroize", +] + +[[package]] +name = "naive-timer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "034a0ad7deebf0c2abcf2435950a6666c3c15ea9d8fad0c0f48efa8a7f843fed" + +[[package]] +name = "named-lock" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40a3eb6b7c682b65d1f631ec3176829d72ab450b3aacdd3f719bf220822e59ac" +dependencies = [ + "libc", + "once_cell", + "parking_lot 0.12.1", + "thiserror", + "widestring", + "winapi 0.3.9", +] + +[[package]] +name = "narwhal-config" +version = "0.1.0-canonical-sui" +dependencies = [ + "fastcrypto", + "fastcrypto-tbls", + "match_opt", + "mysten-network", + "mysten-util-mem", + "narwhal-crypto", + "rand 0.8.5", + "serde 1.0.192", + "serde_json", + "thiserror", + "tracing", + "workspace-hack", +] + +[[package]] +name = "narwhal-crypto" +version = "0.1.0-canonical-sui" +dependencies = [ + "bcs 0.1.6", + "fastcrypto", + "fastcrypto-tbls", + "serde 1.0.192", + "shared-crypto", + "workspace-hack", +] + +[[package]] +name = "narwhal-executor" +version = "0.1.0-canonical-sui" +dependencies = [ + "async-trait", + "bcs 0.1.6", + "bincode", + "bytes", + "fastcrypto", + "futures", + "mockall", + "mysten-metrics", + "narwhal-config", + "narwhal-crypto", + "narwhal-network", + "narwhal-primary", + "narwhal-storage", + "narwhal-types", + "prometheus", + "serde 1.0.192", + "sui-protocol-config", + "thiserror", + "tokio", + "tonic 0.10.2", + "tracing", + "typed-store", + "workspace-hack", +] + +[[package]] +name = "narwhal-network" +version = "0.1.0-canonical-sui" +dependencies = [ + "anemo", + "anemo-tower", + "anyhow", + "async-trait", + "axum", + "axum-server", + "backoff", + "bytes", + "dashmap", + "futures", + "mysten-common", + "mysten-metrics", + "narwhal-crypto", + "narwhal-types", + "parking_lot 0.12.1", + "prometheus", + "quinn-proto", + "rand 0.8.5", + "sui-macros", + "tokio", + "tower", + "tracing", + "workspace-hack", +] + +[[package]] +name = "narwhal-node" +version = "0.1.0-canonical-sui" +dependencies = [ + "anemo", + "arc-swap", + "async-trait", + "axum", + "bytes", + "cfg-if", + "clap 4.4.10", + "eyre", + "fastcrypto", + "futures", + "mysten-metrics", + "mysten-network", + "narwhal-config", + "narwhal-crypto", + "narwhal-executor", + "narwhal-network", + "narwhal-primary", + "narwhal-storage", + "narwhal-types", + "narwhal-worker", + "prometheus", + "rand 0.8.5", + "reqwest", + "sui-keys", + "sui-protocol-config", + "sui-types", + "telemetry-subscribers", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "tracing-subscriber", + "url", + "workspace-hack", +] + +[[package]] +name = "narwhal-primary" +version = "0.1.0-canonical-sui" +dependencies = [ + "anemo", + "anemo-tower", + "anyhow", + "async-trait", + "backoff", + "bcs 0.1.6", + "bytes", + "cfg-if", + "fastcrypto", + "fastcrypto-tbls", + "futures", + "governor", + "itertools 0.10.5", + "mysten-common", + "mysten-metrics", + "mysten-network", + "narwhal-config", + "narwhal-crypto", + "narwhal-network", + "narwhal-storage", + "narwhal-types", + "once_cell", + "parking_lot 0.12.1", + "prometheus", + "rand 0.8.5", + "sui-macros", + "sui-protocol-config", + "tap", + "thiserror", + "tokio", + "tokio-stream", + "tower", + "tracing", + "typed-store", + "workspace-hack", +] + +[[package]] +name = "narwhal-storage" +version = "0.1.0-canonical-sui" +dependencies = [ + "fastcrypto", + "futures", + "lru 0.10.1", + "mysten-common", + "narwhal-config", + "narwhal-types", + "parking_lot 0.12.1", + "prometheus", + "sui-macros", + "tap", + "tempfile", + "tokio", + "tracing", + "typed-store", + "workspace-hack", +] + +[[package]] +name = "narwhal-test-utils" +version = "0.1.0-canonical-sui" +dependencies = [ + "anemo", + "fastcrypto", + "fdlimit", + "indexmap 1.9.3", + "itertools 0.10.5", + "mysten-metrics", + "mysten-network", + "narwhal-config", + "narwhal-crypto", + "narwhal-executor", + "narwhal-network", + "narwhal-node", + "narwhal-primary", + "narwhal-storage", + "narwhal-types", + "narwhal-worker", + "once_cell", + "prometheus", + "rand 0.8.5", + "sui-protocol-config", + "telemetry-subscribers", + "tempfile", + "tokio", + "tonic 0.10.2", + "tracing", + "typed-store", + "workspace-hack", +] + +[[package]] +name = "narwhal-types" +version = "0.1.0-canonical-sui" +dependencies = [ + "anemo", + "anemo-build", + "anyhow", + "base64 0.21.5", + "bcs 0.1.6", + "bytes", + "derive_builder", + "enum_dispatch", + "fastcrypto", + "fastcrypto-tbls", + "futures", + "indexmap 1.9.3", + "mockall", + "mysten-common", + "mysten-metrics", + "mysten-network", + "mysten-util-mem", + "narwhal-config", + "narwhal-crypto", + "once_cell", + "prometheus", + "proptest", + "proptest-derive", + "prost 0.12.3", + "prost-build", + "protobuf-src", + "rand 0.8.5", + "roaring", + "rustversion", + "serde 1.0.192", + "serde_with", + "sui-protocol-config", + "thiserror", + "tokio", + "tonic 0.10.2", + "tonic-build", + "tracing", + "typed-store", + "workspace-hack", +] + +[[package]] +name = "narwhal-worker" +version = "0.1.0-canonical-sui" +dependencies = [ + "anemo", + "anemo-tower", + "anyhow", + "arc-swap", + "async-trait", + "byteorder", + "bytes", + "eyre", + "fastcrypto", + "futures", + "governor", + "itertools 0.10.5", + "mysten-metrics", + "mysten-network", + "narwhal-config", + "narwhal-crypto", + "narwhal-network", + "narwhal-types", + "prometheus", + "rand 0.8.5", + "sui-protocol-config", + "tap", + "thiserror", + "tokio", + "tonic 0.10.2", + "tower", + "tracing", + "typed-store", + "workspace-hack", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static 1.4.0", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "newline-converter" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f71d09d5c87634207f894c6b31b6a2b2c64ea3bdcf71bd5599fdbbe1600c00f" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if", + "libc", + "memoffset 0.6.5", +] + +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nom" +version = "5.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" +dependencies = [ + "lexical-core", + "memchr", + "version_check", +] + +[[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 = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "ntapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi 0.3.9", +] + +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits 0.2.17", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits 0.2.17", + "rand 0.8.5", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static 1.4.0", + "libm", + "num-integer", + "num-iter", + "num-traits 0.2.17", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-complex" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +dependencies = [ + "num-traits 0.2.17", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec 0.7.4", + "itoa", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits 0.2.17", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits 0.2.17", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits 0.2.17", +] + +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +dependencies = [ + "num-traits 0.2.17", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.3", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro-crate 1.1.3", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "object_store" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f930c88a43b1c3f6e776dfe495b4afab89882dbc81530c632db2ed65451ebcb4" +dependencies = [ + "async-trait", + "base64 0.21.5", + "bytes", + "chrono", + "futures", + "humantime", + "hyper", + "itertools 0.11.0", + "parking_lot 0.12.1", + "percent-encoding", + "quick-xml 0.30.0", + "rand 0.8.5", + "reqwest", + "ring 0.16.20", + "rustls-pemfile 1.0.4", + "serde 1.0.192", + "serde_json", + "snafu", + "tokio", + "tracing", + "url", + "walkdir", +] + +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" +dependencies = [ + "bitflags 2.4.1", + "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 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "opentelemetry" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9591d937bc0e6d2feb6f71a559540ab300ea49955229c347a517a28d27784c54" +dependencies = [ + "opentelemetry_api", + "opentelemetry_sdk", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e5e5a5c4135864099f3faafbe939eb4d7f9b80ebf68a8448da961b32a7c1275" +dependencies = [ + "async-trait", + "futures-core", + "http", + "opentelemetry-proto", + "opentelemetry-semantic-conventions", + "opentelemetry_api", + "opentelemetry_sdk", + "prost 0.11.9", + "thiserror", + "tokio", + "tonic 0.9.2", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e3f814aa9f8c905d0ee4bde026afd3b2577a97c10e1699912e3e44f0c4cbeb" +dependencies = [ + "opentelemetry_api", + "opentelemetry_sdk", + "prost 0.11.9", + "tonic 0.9.2", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73c9f9340ad135068800e7f1b24e9e09ed9e7143f5bf8518ded3d3ec69789269" +dependencies = [ + "opentelemetry", +] + +[[package]] +name = "opentelemetry_api" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a81f725323db1b1206ca3da8bb19874bbd3f57c3bcd59471bfb04525b265b9b" +dependencies = [ + "futures-channel", + "futures-util", + "indexmap 1.9.3", + "js-sys", + "once_cell", + "pin-project-lite", + "thiserror", + "urlencoding", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa8e705a0612d48139799fcbaba0d4a90f06277153e43dd2bdc16c6f0edd8026" +dependencies = [ + "async-trait", + "crossbeam-channel", + "futures-channel", + "futures-executor", + "futures-util", + "once_cell", + "opentelemetry_api", + "ordered-float 3.9.2", + "percent-encoding", + "rand 0.8.5", + "regex", + "serde_json", + "thiserror", + "tokio", + "tokio-stream", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits 0.2.17", +] + +[[package]] +name = "ordered-float" +version = "3.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" +dependencies = [ + "num-traits 0.2.17", +] + +[[package]] +name = "ouroboros" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeff60e3e37407a80ead3e9458145b456e978c4068cddbfea6afb48572962ca" +dependencies = [ + "ouroboros_macro 0.9.5", + "stable_deref_trait", +] + +[[package]] +name = "ouroboros" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" +dependencies = [ + "aliasable", + "ouroboros_macro 0.15.6", +] + +[[package]] +name = "ouroboros" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2ba07320d39dfea882faa70554b4bd342a5f273ed59ba7c1c6b4c840492c954" +dependencies = [ + "aliasable", + "ouroboros_macro 0.17.2", + "static_assertions", +] + +[[package]] +name = "ouroboros_macro" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03f2cb802b5bdfdf52f1ffa0b54ce105e4d346e91990dd571f86c91321ad49e2" +dependencies = [ + "Inflector", + "proc-macro-error", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "ouroboros_macro" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" +dependencies = [ + "Inflector", + "proc-macro-error", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "ouroboros_macro" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", + "primeorder", + "sha2 0.10.8", +] + +[[package]] +name = "papergrid" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae7891b22598926e4398790c8fe6447930c72a67d36d983a49d6ce682ce83290" +dependencies = [ + "bytecount", + "fnv", + "unicode-width", +] + +[[package]] +name = "parity-scale-codec" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" +dependencies = [ + "arrayvec 0.7.4", + "bitvec 0.20.4", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde 1.0.192", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" +dependencies = [ + "proc-macro-crate 1.1.3", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.9", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi 0.3.9", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "parse-zoneinfo" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" +dependencies = [ + "regex", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pbjson" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "599fe9aefc2ca0df4a96179b3075faee2cacb89d4cf947a00b9a89152dfffc9d" +dependencies = [ + "base64 0.13.1", + "serde 1.0.192", +] + +[[package]] +name = "pbkdf2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +dependencies = [ + "crypto-mac 0.8.0", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + +[[package]] +name = "pem-rfc7468" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" +dependencies = [ + "base64ct", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pest" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "pest_meta" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" +dependencies = [ + "once_cell", + "pest", + "sha2 0.10.8", +] + +[[package]] +name = "petgraph" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset 0.2.0", + "indexmap 1.9.3", +] + +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset 0.4.2", + "indexmap 2.1.0", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs1" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff33bdbdfc54cc98a2eca766ebdec3e1b8fb7387523d5c9c9a2891da856f719" +dependencies = [ + "der 0.6.1", + "pkcs8 0.9.0", + "spki 0.6.0", + "zeroize", +] + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.8", + "spki 0.7.3", +] + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "png" +version = "0.17.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "poem" +version = "1.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a56df40b79ebdccf7986b337f9b0e51ac55cd5e9d21fb20b6aa7c7d49741854" +dependencies = [ + "anyhow", + "async-trait", + "bytes", + "chrono", + "cookie", + "futures-util", + "headers", + "http", + "hyper", + "mime", + "multer", + "parking_lot 0.12.1", + "percent-encoding", + "pin-project-lite", + "poem-derive", + "quick-xml 0.26.0", + "regex", + "rfc7239", + "rustls-pemfile 1.0.4", + "serde 1.0.192", + "serde_json", + "serde_urlencoded", + "serde_yaml 0.9.27", + "smallvec", + "tempfile", + "thiserror", + "time", + "tokio", + "tokio-rustls 0.24.1", + "tokio-stream", + "tokio-util 0.7.10", + "tracing", +] + +[[package]] +name = "poem-derive" +version = "1.3.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ddcf4680d8d867e1e375116203846acb088483fa2070244f90589f458bbb31" +dependencies = [ + "proc-macro-crate 2.0.0", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "poem-openapi" +version = "2.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e26f78b6195ea1b7a16f46bda1961c598e5a66912f2aa1b8b7a2f395aebb9fc" +dependencies = [ + "base64 0.21.5", + "bytes", + "derive_more", + "futures-util", + "mime", + "num-traits 0.2.17", + "poem", + "poem-openapi-derive", + "quick-xml 0.26.0", + "regex", + "serde 1.0.192", + "serde_json", + "serde_urlencoded", + "serde_yaml 0.9.27", + "thiserror", + "tokio", + "url", +] + +[[package]] +name = "poem-openapi-derive" +version = "2.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88c3e2975c930dc72c024e75b230c3b6058fb3a746d5739b83aa8f28ab1a42d4" +dependencies = [ + "darling 0.14.4", + "http", + "indexmap 1.9.3", + "mime", + "proc-macro-crate 1.1.3", + "proc-macro2 1.0.69", + "quote 1.0.33", + "regex", + "syn 1.0.109", + "thiserror", +] + +[[package]] +name = "polyval" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "portable-atomic" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bccab0e7fd7cc19f820a1c8c91720af652d0c88dc9664dd72aef2614f04af3b" + +[[package]] +name = "poseidon-ark" +version = "0.0.1" +source = "git+https://github.com/arnaucube/poseidon-ark.git?rev=ff7f5e05d55667b4ffba129b837da780c4c5c849#ff7f5e05d55667b4ffba129b837da780c4c5c849" +dependencies = [ + "ark-bn254", + "ark-ff", + "ark-std", +] + +[[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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "predicates" +version = "2.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +dependencies = [ + "difflib", + "float-cmp", + "itertools 0.10.5", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "pretty" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9940b913ee56ddd94aec2d3cd179dd47068236f42a1a6415ccf9d880ce2a61" +dependencies = [ + "arrayvec 0.5.2", + "typed-arena", +] + +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2 1.0.69", + "syn 1.0.109", +] + +[[package]] +name = "prettyplease" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +dependencies = [ + "proc-macro2 1.0.69", + "syn 2.0.38", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve 0.13.8", +] + +[[package]] +name = "primitive-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-serde", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml 0.5.11", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "procfs" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1de8dacb0873f77e6aefc6d71e044761fcc68060290f5b1089fcdf84626bb69" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "chrono", + "flate2", + "hex", + "lazy_static 1.4.0", + "rustix 0.36.17", +] + +[[package]] +name = "prometheus" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static 1.4.0", + "memchr", + "parking_lot 0.12.1", + "protobuf", + "thiserror", +] + +[[package]] +name = "prometheus-closure-metric" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "prometheus", + "protobuf", + "workspace-hack", +] + +[[package]] +name = "proptest" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags 2.4.1", + "lazy_static 1.4.0", + "num-traits 0.2.17", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_xorshift", + "regex-syntax 0.8.2", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "proptest-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90b46295382dc76166cb7cf2bb4a97952464e4b7ed5a43e6cd34e1fec3349ddc" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive 0.11.9", +] + +[[package]] +name = "prost" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" +dependencies = [ + "bytes", + "prost-derive 0.12.3", +] + +[[package]] +name = "prost-build" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2" +dependencies = [ + "bytes", + "heck 0.4.1", + "itertools 0.10.5", + "log", + "multimap", + "once_cell", + "petgraph 0.6.4", + "prettyplease 0.2.15", + "prost 0.12.3", + "prost-types", + "regex", + "syn 2.0.38", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "prost-derive" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "prost-types" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" +dependencies = [ + "prost 0.12.3", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" +dependencies = [ + "bytes", +] + +[[package]] +name = "protobuf-src" +version = "1.1.0+21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7ac8852baeb3cc6fb83b93646fb93c0ffe5d14bf138c945ceb4b9948ee0e3c1" +dependencies = [ + "autotools", +] + +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "ptree" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0de80796b316aec75344095a6d2ef68ec9b8f573b9e7adc821149ba3598e270" +dependencies = [ + "ansi_term", + "atty", + "config", + "directories", + "petgraph 0.6.4", + "serde 1.0.192", + "serde-value", + "tint", +] + +[[package]] +name = "publicsuffix" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457" +dependencies = [ + "idna 0.3.0", + "psl-types", +] + +[[package]] +name = "qstring" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "quanta" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17e662a7a8291a865152364c20c7abc5e60486ab2001e8ec10b24862de0b9ab" +dependencies = [ + "crossbeam-utils", + "libc", + "mach2", + "once_cell", + "raw-cpuid", + "wasi 0.11.0+wasi-snapshot-preview1", + "web-sys", + "winapi 0.3.9", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-xml" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8533f14c8382aaad0d592c812ac3b826162128b65662331e1127b45c3d18536b" +dependencies = [ + "memchr", +] + +[[package]] +name = "quick-xml" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +dependencies = [ + "memchr", + "serde 1.0.192", +] + +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", + "serde 1.0.192", +] + +[[package]] +name = "quinn" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" +dependencies = [ + "bytes", + "futures-io", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.21.9", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a" +dependencies = [ + "bytes", + "rand 0.8.5", + "ring 0.16.20", + "rustc-hash", + "rustls 0.21.9", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" +dependencies = [ + "bytes", + "libc", + "socket2 0.5.5", + "tracing", + "windows-sys 0.48.0", +] + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2 1.0.69", +] + +[[package]] +name = "radium" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[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 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.11", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "raw-cpuid" +version = "10.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rcgen" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" +dependencies = [ + "pem", + "ring 0.16.20", + "time", + "yasna", +] + +[[package]] +name = "readonly" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8f439da1766942fe069954da6058b2e6c1760eb878bae76f5be9fc29f56f574" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "real_tokio" +version = "1.28.1" +source = "git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e4693500118d5e79ce098ee6dfc2c48f3ef19e45#e4693500118d5e79ce098ee6dfc2c48f3ef19e45" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio 0.8.9", + "num_cpus", + "parking_lot 0.12.1", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.4.10", + "tokio-macros 2.1.0 (git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e4693500118d5e79ce098ee6dfc2c48f3ef19e45)", + "windows-sys 0.48.0", +] + +[[package]] +name = "redis" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa8455fa3621f6b41c514946de66ea0531f57ca017b2e6c7cc368035ea5b46df" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "combine", + "futures", + "futures-util", + "itoa", + "percent-encoding", + "pin-project-lite", + "ryu", + "sha1_smol", + "tokio", + "tokio-util 0.7.10", + "url", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom 0.2.11", + "libredox", + "thiserror", +] + +[[package]] +name = "ref-cast" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acde58d073e9c79da00f2b5b84eed919c8326832648a5b109b3fce1bb1175280" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "reqwest" +version = "0.11.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +dependencies = [ + "base64 0.21.5", + "bytes", + "cookie", + "cookie_store", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls 0.24.2", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls 0.21.9", + "rustls-pemfile 1.0.4", + "serde 1.0.192", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-rustls 0.24.1", + "tokio-util 0.7.10", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots 0.25.3", + "winreg", +] + +[[package]] +name = "reqwest-middleware" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a3e86aa6053e59030e7ce2d2a3b258dd08fc2d337d52f73f6cb480f5858690" +dependencies = [ + "anyhow", + "async-trait", + "http", + "reqwest", + "serde 1.0.192", + "task-local-extensions", + "thiserror", +] + +[[package]] +name = "reqwest-retry" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6a11c05102e5bec712c0619b8c7b7eda8b21a558a0bd981ceee15c38df8be4" +dependencies = [ + "anyhow", + "async-trait", + "chrono", + "futures", + "getrandom 0.2.11", + "http", + "hyper", + "parking_lot 0.11.2", + "reqwest", + "reqwest-middleware", + "retry-policies", + "task-local-extensions", + "tokio", + "tracing", + "wasm-timer", +] + +[[package]] +name = "retain_mut" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c31b5c4033f8fdde8700e4657be2c497e7288f01515be52168c631e2e4d4086" + +[[package]] +name = "retry-policies" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e09bbcb5003282bcb688f0bae741b278e9c7e8f378f561522c9806c58e075d9b" +dependencies = [ + "anyhow", + "chrono", + "rand 0.8.5", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac 0.12.1", + "zeroize", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle", +] + +[[package]] +name = "rfc7239" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "087317b3cf7eb481f13bd9025d729324b7cd068d6f470e2d76d049e191f5ba47" +dependencies = [ + "uncased", +] + +[[package]] +name = "rgb" +version = "0.8.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi 0.3.9", +] + +[[package]] +name = "ring" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +dependencies = [ + "cc", + "getrandom 0.2.11", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.48.0", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "roaring" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6106b5cf8587f5834158895e9715a3c6c9716c8aefab57f1f7680917191c7873" +dependencies = [ + "bytemuck", + "byteorder", + "retain_mut", +] + +[[package]] +name = "rocksdb" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" +dependencies = [ + "libc", + "librocksdb-sys", +] + +[[package]] +name = "rsa" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55a77d189da1fee555ad95b7e50e7457d91c0e089ec68ca69ad2989413bbdab4" +dependencies = [ + "byteorder", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits 0.2.17", + "pkcs1", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sha2 0.10.8", + "signature 2.2.0", + "subtle", + "zeroize", +] + +[[package]] +name = "rusoto_core" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db30db44ea73551326269adcf7a2169428a054f14faf9e1768f2163494f2fa2" +dependencies = [ + "async-trait", + "base64 0.13.1", + "bytes", + "crc32fast", + "futures", + "http", + "hyper", + "hyper-rustls 0.23.2", + "lazy_static 1.4.0", + "log", + "rusoto_credential", + "rusoto_signature", + "rustc_version", + "serde 1.0.192", + "serde_json", + "tokio", + "xml-rs", +] + +[[package]] +name = "rusoto_credential" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee0a6c13db5aad6047b6a44ef023dbbc21a056b6dab5be3b79ce4283d5c02d05" +dependencies = [ + "async-trait", + "chrono", + "dirs-next", + "futures", + "hyper", + "serde 1.0.192", + "serde_json", + "shlex", + "tokio", + "zeroize", +] + +[[package]] +name = "rusoto_kms" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e1fc19cfcfd9f6b2f96e36d5b0dddda9004d2cbfc2d17543e3b9f10cc38fce8" +dependencies = [ + "async-trait", + "bytes", + "futures", + "rusoto_core", + "serde 1.0.192", + "serde_json", +] + +[[package]] +name = "rusoto_signature" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ae95491c8b4847931e291b151127eccd6ff8ca13f33603eb3d0035ecb05272" +dependencies = [ + "base64 0.13.1", + "bytes", + "chrono", + "digest 0.9.0", + "futures", + "hex", + "hmac 0.11.0", + "http", + "hyper", + "log", + "md-5 0.9.1", + "percent-encoding", + "pin-project-lite", + "rusoto_credential", + "rustc_version", + "serde 1.0.192", + "sha2 0.9.9", + "tokio", +] + +[[package]] +name = "rust-ini" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[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.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom 7.1.3", +] + +[[package]] +name = "rustix" +version = "0.36.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.1.4", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustix" +version = "0.38.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys 0.4.11", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustls" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" +dependencies = [ + "log", + "ring 0.16.20", + "sct", + "webpki", +] + +[[package]] +name = "rustls" +version = "0.21.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" +dependencies = [ + "log", + "ring 0.17.5", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile 1.0.4", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360" +dependencies = [ + "base64 0.13.1", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.5", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring 0.17.5", + "untrusted 0.9.0", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[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 = "rustyline" +version = "9.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db7826789c0e25614b03e5a54a0717a86f9ff6e6e5247f92b369472869320039" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "clipboard-win", + "dirs-next", + "fd-lock", + "libc", + "log", + "memchr", + "nix", + "radix_trie", + "scopeguard", + "smallvec", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "winapi 0.3.9", +] + +[[package]] +name = "rustyline-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "107c3d5d7f370ac09efa62a78375f94d94b8a33c61d8c278b96683fb4dbf2d8d" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "schemars" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +dependencies = [ + "dyn-clone", + "either", + "schemars_derive", + "serde 1.0.192", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring 0.17.5", + "untrusted 0.9.0", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct 0.1.1", + "der 0.6.1", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.8", + "generic-array", + "pkcs8 0.10.2", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" +dependencies = [ + "bitcoin_hashes", + "rand 0.8.5", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" +dependencies = [ + "cc", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "self_update" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b28d58e73c427061f46c801176f54349be3c1a2818cf549e1d9bcac37eef7bca" +dependencies = [ + "hyper", + "indicatif", + "log", + "quick-xml 0.22.0", + "regex", + "reqwest", + "semver", + "serde_json", + "tempfile", + "zip", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +dependencies = [ + "serde 1.0.192", +] + +[[package]] +name = "serde" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" + +[[package]] +name = "serde" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-generate" +version = "0.20.6" +source = "git+https://github.com/aptos-labs/serde-reflection?rev=839aed62a20ddccf043c08961cfe74875741ccba#839aed62a20ddccf043c08961cfe74875741ccba" +dependencies = [ + "bcs 0.1.6", + "bincode", + "heck 0.3.3", + "include_dir 0.6.2", + "maplit", + "serde 1.0.192", + "serde-reflection 0.3.5", + "serde_bytes", + "serde_yaml 0.8.26", + "structopt", + "textwrap 0.13.4", +] + +[[package]] +name = "serde-hjson" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" +dependencies = [ + "lazy_static 1.4.0", + "num-traits 0.1.43", + "regex", + "serde 0.8.23", +] + +[[package]] +name = "serde-name" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12c47087018ec281d1cdab673d36aea22d816b54d498264029c05d5fa1910da6" +dependencies = [ + "serde 1.0.192", + "thiserror", +] + +[[package]] +name = "serde-name" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b5b14ebbcc4e4f2b3642fa99c388649da58d1dc3308c7d109f39f565d1710f0" +dependencies = [ + "serde 1.0.192", + "thiserror", +] + +[[package]] +name = "serde-reflection" +version = "0.3.5" +source = "git+https://github.com/aptos-labs/serde-reflection?rev=839aed62a20ddccf043c08961cfe74875741ccba#839aed62a20ddccf043c08961cfe74875741ccba" +dependencies = [ + "once_cell", + "serde 1.0.192", + "thiserror", +] + +[[package]] +name = "serde-reflection" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05a5f801ac62a51a49d378fdb3884480041b99aced450b28990673e8ff99895" +dependencies = [ + "once_cell", + "serde 1.0.192", + "thiserror", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float 2.10.1", + "serde 1.0.192", +] + +[[package]] +name = "serde_bytes" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +dependencies = [ + "serde 1.0.192", +] + +[[package]] +name = "serde_derive" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] [[package]] -name = "futures" -version = "0.3.29" +name = "serde_json" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", + "indexmap 2.1.0", + "itoa", + "ryu", + "serde 1.0.192", ] [[package]] -name = "futures-channel" -version = "0.3.29" +name = "serde_merge" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "606e91878516232ac3b16c12e063d4468d762f16d77e7aef14a1f2326c5f409b" dependencies = [ - "futures-core", - "futures-sink", + "serde 1.0.192", + "serde_json", + "thiserror", ] [[package]] -name = "futures-core" -version = "0.3.29" +name = "serde_path_to_error" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde 1.0.192", +] [[package]] -name = "futures-executor" -version = "0.3.29" +name = "serde_repr" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" dependencies = [ - "futures-core", - "futures-task", - "futures-util", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] -name = "futures-io" -version = "0.3.29" +name = "serde_spanned" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +dependencies = [ + "serde 1.0.192", +] [[package]] -name = "futures-macro" -version = "0.3.29" +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 1.0.192", +] + +[[package]] +name = "serde_with" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" +dependencies = [ + "base64 0.13.1", + "chrono", + "hex", + "indexmap 1.9.3", + "serde 1.0.192", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ - "proc-macro2", - "quote", + "darling 0.20.3", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 2.0.38", ] [[package]] -name = "futures-sink" -version = "0.3.29" +name = "serde_yaml" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap 1.9.3", + "ryu", + "serde 1.0.192", + "yaml-rust", +] [[package]] -name = "futures-task" -version = "0.3.29" +name = "serde_yaml" +version = "0.9.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" +dependencies = [ + "indexmap 2.1.0", + "itoa", + "ryu", + "serde 1.0.192", + "unsafe-libyaml", +] [[package]] -name = "futures-util" -version = "0.3.29" +name = "sha-1" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", ] [[package]] -name = "generic-array" -version = "0.14.7" +name = "sha1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "typenum", - "version_check", - "zeroize", + "cfg-if", + "cpufeatures", + "digest 0.10.7", ] [[package]] -name = "getrandom" -version = "0.2.11" +name = "sha1_smol" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[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.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug", +] + +[[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 = "shadow-rs" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c0ea0c68418544f725eba5401a5b965a2263254c92458d04aeae74e9d88ff4e" +dependencies = [ + "const_format", + "git2", + "is_debug", + "time", + "tzdb", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static 1.4.0", +] + +[[package]] +name = "shared-crypto" +version = "0.0.0-canonical-sui" +dependencies = [ + "bcs 0.1.6", + "eyre", + "fastcrypto", + "serde 1.0.192", + "serde_repr", + "workspace-hack", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "shellexpand" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" +dependencies = [ + "dirs 5.0.1", +] + +[[package]] +name = "shlex" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio 0.7.14", + "mio 0.8.9", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[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 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "similar" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597" +dependencies = [ + "bstr 0.2.17", + "unicode-segmentation", +] + +[[package]] +name = "similar-asserts" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e041bb827d1bfca18f213411d51b665309f1afb37a04a5d1464530e13779fc0f" +dependencies = [ + "console", + "similar", +] + +[[package]] +name = "simple_asn1" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +dependencies = [ + "num-bigint", + "num-traits 0.2.17", + "thiserror", + "time", +] + +[[package]] +name = "simplelog" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bc0ffd69814a9b251d43afcabf96dad1b29f5028378056257be9e3fecc9f720" +dependencies = [ + "chrono", + "log", + "termcolor", +] + +[[package]] +name = "simulacrum" +version = "0.1.0-canonical-sui" dependencies = [ - "cfg-if", - "libc", - "wasi", + "anyhow", + "async-trait", + "bcs 0.1.6", + "fastcrypto", + "futures", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "narwhal-config", + "once_cell", + "prometheus", + "rand 0.8.5", + "serde 1.0.192", + "shared-crypto", + "sui-config", + "sui-execution", + "sui-framework", + "sui-genesis-builder", + "sui-keys", + "sui-protocol-config", + "sui-storage", + "sui-swarm-config", + "sui-transaction-checks", + "sui-types", + "tracing", + "workspace-hack", ] [[package]] -name = "gimli" -version = "0.28.0" +name = "siphasher" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] -name = "glob" -version = "0.3.1" +name = "sized-chunks" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] [[package]] -name = "group" -version = "0.13.0" +name = "slab" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "ff", - "rand_core", - "subtle", + "autocfg", ] [[package]] -name = "h2" -version = "0.3.22" +name = "slip10_ed25519" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "4be0ff28bf14f9610a342169084e87a4f435ad798ec528dc7579a3678fa9dc9a" dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap 2.1.0", - "slab", - "tokio", - "tokio-util", - "tracing", + "hmac-sha512", ] [[package]] -name = "hashbrown" -version = "0.12.3" +name = "slug" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "3bd94acec9c8da640005f8e135a39fc0372e74535e6b368b7a04b875f784c8c4" +dependencies = [ + "deunicode", + "wasm-bindgen", +] [[package]] -name = "hashbrown" -version = "0.14.2" +name = "smallvec" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] -name = "heck" -version = "0.4.1" +name = "smawk" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] -name = "hermit-abi" -version = "0.1.19" +name = "snafu" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" dependencies = [ - "libc", + "doc-comment", + "snafu-derive", ] [[package]] -name = "hermit-abi" -version = "0.3.3" +name = "snafu-derive" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", +] [[package]] -name = "hex" -version = "0.4.3" +name = "snap" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" [[package]] -name = "hmac" -version = "0.12.1" +name = "socket2" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ - "digest", + "libc", + "winapi 0.3.9", ] [[package]] -name = "http" -version = "0.2.11" +name = "socket2" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ - "bytes", - "fnv", - "itoa", + "libc", + "windows-sys 0.48.0", ] [[package]] -name = "http-body" -version = "0.4.5" +name = "soketto" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" dependencies = [ + "base64 0.13.1", "bytes", + "futures", "http", - "pin-project-lite", + "httparse", + "log", + "rand 0.8.5", + "sha-1", ] [[package]] -name = "httparse" -version = "1.8.0" +name = "spin" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] -name = "httpdate" -version = "1.0.3" +name = "spin" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] -name = "hyper" -version = "0.14.27" +name = "spki" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.4.10", - "tokio", - "tower-service", - "tracing", - "want", + "base64ct", + "der 0.6.1", ] [[package]] -name = "hyper-timeout" -version = "0.4.1" +name = "spki" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ - "hyper", - "pin-project-lite", - "tokio", - "tokio-io-timeout", + "base64ct", + "der 0.7.8", ] [[package]] -name = "hyper-tls" -version = "0.5.0" +name = "stable_deref_trait" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] -name = "iana-time-zone" -version = "0.1.58" +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" +name = "status-line" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +checksum = "a20cc99bbe608305546a850ec4352907279a8b8044f9c13ae58bd0a8ab46ebc1" dependencies = [ - "cc", + "ansi-escapes", + "atty", ] [[package]] -name = "ident_case" -version = "1.0.1" +name = "str-buf" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" [[package]] -name = "idna" -version = "0.4.0" +name = "str_stack" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] +checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" [[package]] -name = "impl-codec" -version = "0.6.0" +name = "strsim" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" -dependencies = [ - "parity-scale-codec", -] +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] -name = "impl-rlp" -version = "0.3.0" +name = "strsim" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" -dependencies = [ - "rlp", -] +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] -name = "impl-serde" -version = "0.4.0" +name = "structopt" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" dependencies = [ - "serde", + "clap 2.34.0", + "lazy_static 1.4.0", + "structopt-derive", ] [[package]] -name = "impl-trait-for-tuples" -version = "0.2.2" +name = "structopt-derive" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ - "proc-macro2", - "quote", + "heck 0.3.3", + "proc-macro-error", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] [[package]] -name = "indexmap" -version = "1.9.3" +name = "strum" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", + "strum_macros", ] [[package]] -name = "indexmap" -version = "2.1.0" +name = "strum_macros" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "equivalent", - "hashbrown 0.14.2", - "serde", + "heck 0.4.1", + "proc-macro2 1.0.69", + "quote 1.0.33", + "rustversion", + "syn 1.0.109", ] [[package]] -name = "ipnet" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" - -[[package]] -name = "itertools" -version = "0.10.5" +name = "subtle" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] -name = "itoa" -version = "1.0.9" +name = "subtle-ng" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" [[package]] -name = "js-sys" -version = "0.3.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +name = "sui" +version = "1.15.0-canonical-sui" dependencies = [ - "wasm-bindgen", + "anemo", + "anyhow", + "async-trait", + "bcs 0.1.6", + "bip32", + "camino", + "clap 4.4.10", + "colored", + "const-str", + "csv", + "fastcrypto", + "fastcrypto-zkp", + "git-version", + "im", + "inquire", + "jemalloc-ctl", + "json_to_table", + "move-core-types 0.0.4-canonical-sui", + "move-package 0.1.0-canonical-sui", + "num-bigint", + "prometheus", + "rand 0.8.5", + "regex", + "reqwest", + "rusoto_core", + "rusoto_kms", + "rustyline", + "rustyline-derive", + "serde 1.0.192", + "serde_json", + "serde_yaml 0.8.26", + "shared-crypto", + "shell-words", + "signature 1.6.4", + "sui-config", + "sui-execution", + "sui-genesis-builder", + "sui-json", + "sui-json-rpc-types", + "sui-keys", + "sui-move", + "sui-move-build", + "sui-protocol-config", + "sui-replay", + "sui-sdk", + "sui-source-validation", + "sui-swarm", + "sui-swarm-config", + "sui-types", + "tabled", + "tap", + "telemetry-subscribers", + "tempfile", + "tokio", + "tracing", + "unescape", + "workspace-hack", ] [[package]] -name = "jsonrpc-core" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" +name = "sui-adapter-latest" +version = "0.1.0-canonical-sui" dependencies = [ - "futures", - "futures-executor", - "futures-util", - "log", - "serde", - "serde_derive", - "serde_json", + "anyhow", + "bcs 0.1.6", + "leb128", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-bytecode-verifier 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-vm-config", + "move-vm-profiler", + "move-vm-runtime 0.1.0-canonical-sui", + "move-vm-types 0.1.0-canonical-sui", + "parking_lot 0.12.1", + "serde 1.0.192", + "sui-macros", + "sui-move-natives-latest", + "sui-protocol-config", + "sui-types", + "sui-verifier-latest", + "tracing", + "workspace-hack", ] [[package]] -name = "k256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b" +name = "sui-adapter-v0" +version = "0.1.0-canonical-sui" dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", + "anyhow", + "bcs 0.1.6", + "leb128", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-bytecode-verifier-v0", + "move-core-types 0.0.4-canonical-sui", + "move-vm-config", + "move-vm-profiler", + "move-vm-runtime-v0", + "move-vm-types 0.1.0-canonical-sui", "once_cell", - "sha2", - "signature", + "parking_lot 0.12.1", + "serde 1.0.192", + "sui-macros", + "sui-move-natives-v0", + "sui-protocol-config", + "sui-types", + "sui-verifier-v0", + "tracing", + "workspace-hack", ] [[package]] -name = "keccak" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +name = "sui-adapter-v1" +version = "0.1.0-canonical-sui" dependencies = [ - "cpufeatures", + "anyhow", + "bcs 0.1.6", + "leb128", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-bytecode-verifier-v1", + "move-core-types 0.0.4-canonical-sui", + "move-vm-config", + "move-vm-profiler", + "move-vm-runtime-v1", + "move-vm-types 0.1.0-canonical-sui", + "parking_lot 0.12.1", + "serde 1.0.192", + "sui-macros", + "sui-move-natives-v1", + "sui-protocol-config", + "sui-types", + "sui-verifier-v1", + "tracing", + "workspace-hack", ] [[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +name = "sui-archival" +version = "0.1.0-canonical-sui" dependencies = [ - "spin 0.5.2", + "anyhow", + "byteorder", + "bytes", + "fastcrypto", + "futures", + "indicatif", + "num_enum", + "object_store", + "prometheus", + "rand 0.8.5", + "serde 1.0.192", + "sui-config", + "sui-simulator", + "sui-storage", + "sui-types", + "tokio", + "tracing", + "workspace-hack", ] [[package]] -name = "libc" -version = "0.2.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" - -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" - -[[package]] -name = "libredox" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +name = "sui-config" +version = "0.0.0-canonical-sui" dependencies = [ - "bitflags 2.4.1", - "libc", - "redox_syscall", + "anemo", + "anyhow", + "bcs 0.1.6", + "csv", + "dirs 4.0.0", + "fastcrypto", + "narwhal-config", + "once_cell", + "prometheus", + "rand 0.8.5", + "serde 1.0.192", + "serde_with", + "serde_yaml 0.8.26", + "sui-keys", + "sui-protocol-config", + "sui-simulator", + "sui-storage", + "sui-types", + "tracing", + "workspace-hack", ] [[package]] -name = "linux-raw-sys" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" - -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +name = "sui-core" +version = "0.1.0-canonical-sui" dependencies = [ - "autocfg", + "anyhow", + "arc-swap", + "async-trait", + "bcs 0.1.6", + "bytes", + "chrono", + "dashmap", + "either", + "enum_dispatch", + "eyre", + "fastcrypto", + "fastcrypto-zkp", + "futures", + "im", + "indexmap 1.9.3", + "itertools 0.10.5", + "lru 0.10.1", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-package 0.1.0-canonical-sui", + "move-symbol-pool 0.1.0-canonical-sui", + "mysten-common", + "mysten-metrics", + "mysten-network", + "mysticeti-core", + "narwhal-config", + "narwhal-crypto", + "narwhal-executor", + "narwhal-network", + "narwhal-node", + "narwhal-test-utils", + "narwhal-types", + "narwhal-worker", + "num_cpus", + "object_store", + "once_cell", + "parking_lot 0.12.1", + "prometheus", + "rand 0.8.5", + "rocksdb", "scopeguard", + "serde 1.0.192", + "serde_json", + "serde_with", + "shared-crypto", + "signature 1.6.4", + "sui-archival", + "sui-config", + "sui-execution", + "sui-framework", + "sui-genesis-builder", + "sui-json-rpc-types", + "sui-macros", + "sui-move-build", + "sui-network", + "sui-protocol-config", + "sui-simulator", + "sui-storage", + "sui-swarm-config", + "sui-transaction-checks", + "sui-types", + "tap", + "telemetry-subscribers", + "tempfile", + "thiserror", + "tokio", + "tokio-retry", + "tokio-stream", + "tracing", + "typed-store", + "typed-store-derive", + "workspace-hack", + "zeroize", ] [[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +name = "sui-enum-compat-util" +version = "0.1.0-canonical-sui" +dependencies = [ + "serde_yaml 0.8.26", + "workspace-hack", +] [[package]] -name = "memchr" -version = "2.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +name = "sui-execution" +version = "0.1.0-canonical-sui" +dependencies = [ + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-verifier 0.1.0-canonical-sui", + "move-bytecode-verifier-v0", + "move-bytecode-verifier-v1", + "move-vm-config", + "move-vm-runtime 0.1.0-canonical-sui", + "move-vm-runtime-v0", + "move-vm-runtime-v1", + "sui-adapter-latest", + "sui-adapter-v0", + "sui-adapter-v1", + "sui-move-natives-latest", + "sui-move-natives-v0", + "sui-move-natives-v1", + "sui-protocol-config", + "sui-types", + "sui-verifier-latest", + "sui-verifier-v0", + "sui-verifier-v1", + "workspace-hack", +] [[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +name = "sui-framework" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "bcs 0.1.6", + "move-binary-format 0.0.3-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-package 0.1.0-canonical-sui", + "once_cell", + "serde 1.0.192", + "sui-move-build", + "sui-types", + "tracing", + "workspace-hack", +] [[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +name = "sui-framework-snapshot" +version = "0.1.0-canonical-sui" +dependencies = [ + "anyhow", + "bcs 0.1.6", + "git-version", + "serde 1.0.192", + "serde_json", + "sui-framework", + "sui-protocol-config", + "sui-types", + "workspace-hack", +] [[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +name = "sui-genesis-builder" +version = "0.0.0-canonical-sui" dependencies = [ - "adler", + "anyhow", + "bcs 0.1.6", + "camino", + "fastcrypto", + "move-binary-format 0.0.3-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "prometheus", + "rand 0.8.5", + "serde 1.0.192", + "serde_with", + "serde_yaml 0.8.26", + "shared-crypto", + "sui-config", + "sui-execution", + "sui-framework", + "sui-framework-snapshot", + "sui-protocol-config", + "sui-simulator", + "sui-types", + "tempfile", + "tracing", + "workspace-hack", ] [[package]] -name = "mio" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +name = "sui-json" +version = "0.0.0-canonical-sui" dependencies = [ - "libc", - "wasi", - "windows-sys", + "anyhow", + "bcs 0.1.6", + "fastcrypto", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "schemars", + "serde 1.0.192", + "serde_json", + "sui-framework", + "sui-types", + "workspace-hack", ] [[package]] -name = "movement" -version = "0.1.0" +name = "sui-json-rpc" +version = "0.0.0-canonical-sui" dependencies = [ "anyhow", + "arc-swap", "async-trait", - "clap", - "dirs", - "reqwest", - "semver", - "serde", - "tempfile", + "axum", + "bcs 0.1.6", + "cached", + "eyre", + "fastcrypto", + "futures", + "hyper", + "itertools 0.10.5", + "jsonrpsee", + "linked-hash-map", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-package 0.1.0-canonical-sui", + "mysten-metrics", + "once_cell", + "prometheus", + "serde 1.0.192", + "serde_json", + "shared-crypto", + "signature 1.6.4", + "sui-core", + "sui-json", + "sui-json-rpc-types", + "sui-open-rpc", + "sui-open-rpc-macros", + "sui-protocol-config", + "sui-storage", + "sui-transaction-builder", + "sui-types", + "tap", + "thiserror", "tokio", + "tower", + "tower-http", + "tracing", + "typed-store-error", + "workspace-hack", ] [[package]] -name = "movement-sdk" -version = "0.1.0" +name = "sui-json-rpc-types" +version = "0.0.0-canonical-sui" dependencies = [ "anyhow", - "async-trait", - "serde", + "bcs 0.1.6", + "colored", + "enum_dispatch", + "fastcrypto", + "itertools 0.10.5", + "json_to_table", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "mysten-metrics", + "schemars", + "serde 1.0.192", + "serde_json", + "serde_with", + "sui-enum-compat-util", + "sui-json", + "sui-macros", + "sui-protocol-config", + "sui-types", + "tabled", + "tracing", + "workspace-hack", ] [[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +name = "sui-keys" +version = "0.0.0-canonical-sui" dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", + "anyhow", + "bip32", + "fastcrypto", + "rand 0.8.5", + "serde 1.0.192", + "serde_json", + "shared-crypto", + "signature 1.6.4", + "slip10_ed25519", + "sui-types", + "tiny-bip39 1.0.0", + "workspace-hack", ] [[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +name = "sui-kvstore" +version = "0.1.0-canonical-sui" dependencies = [ - "memchr", - "minimal-lexical", + "anyhow", + "async-trait", + "aws-config", + "aws-sdk-dynamodb", + "aws-sdk-s3", + "backoff", + "base64-url", + "bcs 0.1.6", + "mysten-metrics", + "prometheus", + "serde 1.0.192", + "sui-config", + "sui-core", + "sui-storage", + "sui-types", + "tokio", + "tracing", + "workspace-hack", ] [[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +name = "sui-macros" +version = "0.7.0-canonical-sui" dependencies = [ - "autocfg", - "num-integer", - "num-traits", + "futures", + "once_cell", + "sui-proc-macros", + "tracing", + "workspace-hack", ] [[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +name = "sui-move" +version = "1.15.0-canonical-sui" dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand", - "smallvec", - "zeroize", + "anyhow", + "clap 4.4.10", + "colored", + "const-str", + "git-version", + "jemalloc-ctl", + "move-binary-format 0.0.3-canonical-sui", + "move-cli 0.1.0-canonical-sui", + "move-disassembler 0.1.0-canonical-sui", + "move-ir-types 0.1.0-canonical-sui", + "move-package 0.1.0-canonical-sui", + "move-prover 0.1.0-canonical-sui", + "move-prover-boogie-backend 0.1.0-canonical-sui", + "move-unit-test 0.1.0-canonical-sui", + "move-vm-runtime 0.1.0-canonical-sui", + "once_cell", + "prometheus", + "serde_json", + "serde_yaml 0.8.26", + "sui-core", + "sui-move-build", + "sui-move-natives-latest", + "sui-protocol-config", + "sui-types", + "telemetry-subscribers", + "tokio", + "tracing", ] [[package]] -name = "num-derive" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +name = "sui-move-build" +version = "0.0.0-canonical-sui" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "anyhow", + "fastcrypto", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-bytecode-verifier 0.1.0-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-compiler 0.0.1-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-ir-types 0.1.0-canonical-sui", + "move-package 0.1.0-canonical-sui", + "move-symbol-pool 0.1.0-canonical-sui", + "serde-reflection 0.3.6", + "sui-types", + "sui-verifier-latest", + "tempfile", + "workspace-hack", ] [[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +name = "sui-move-natives-latest" +version = "0.1.0-canonical-sui" dependencies = [ - "autocfg", - "num-traits", + "bcs 0.1.6", + "better_any", + "fastcrypto", + "fastcrypto-zkp", + "linked-hash-map", + "move-binary-format 0.0.3-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-stdlib 0.1.1-canonical-sui", + "move-vm-runtime 0.1.0-canonical-sui", + "move-vm-types 0.1.0-canonical-sui", + "smallvec", + "sui-protocol-config", + "sui-types", + "workspace-hack", ] [[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +name = "sui-move-natives-v0" +version = "0.1.0-canonical-sui" dependencies = [ - "autocfg", - "num-integer", - "num-traits", + "bcs 0.1.6", + "better_any", + "fastcrypto", + "fastcrypto-zkp", + "linked-hash-map", + "move-binary-format 0.0.3-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-stdlib-v0", + "move-vm-runtime-v0", + "move-vm-types 0.1.0-canonical-sui", + "smallvec", + "sui-protocol-config", + "sui-types", + "workspace-hack", ] [[package]] -name = "num-traits" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +name = "sui-move-natives-v1" +version = "0.1.0-canonical-sui" dependencies = [ - "autocfg", - "libm", + "bcs 0.1.6", + "better_any", + "fastcrypto", + "fastcrypto-zkp", + "linked-hash-map", + "move-binary-format 0.0.3-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-stdlib-v1", + "move-vm-runtime-v1", + "move-vm-types 0.1.0-canonical-sui", + "smallvec", + "sui-protocol-config", + "sui-types", + "workspace-hack", ] [[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +name = "sui-network" +version = "0.0.0-canonical-sui" dependencies = [ - "hermit-abi 0.3.3", - "libc", + "anemo", + "anemo-build", + "anemo-tower", + "anyhow", + "dashmap", + "futures", + "governor", + "mysten-metrics", + "mysten-network", + "prometheus", + "rand 0.8.5", + "serde 1.0.192", + "sui-archival", + "sui-config", + "sui-storage", + "sui-swarm-config", + "sui-types", + "tap", + "tokio", + "tonic 0.10.2", + "tonic-build", + "tower", + "tracing", + "workspace-hack", ] [[package]] -name = "num_enum" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +name = "sui-node" +version = "1.15.0-canonical-sui" dependencies = [ - "num_enum_derive", + "anemo", + "anemo-tower", + "anyhow", + "arc-swap", + "axum", + "clap 4.4.10", + "const-str", + "fastcrypto", + "fastcrypto-zkp", + "futures", + "git-version", + "humantime", + "mysten-common", + "mysten-metrics", + "mysten-network", + "narwhal-network", + "prometheus", + "reqwest", + "serde 1.0.192", + "snap", + "sui-archival", + "sui-config", + "sui-core", + "sui-json-rpc", + "sui-kvstore", + "sui-macros", + "sui-network", + "sui-protocol-config", + "sui-rest-api", + "sui-simulator", + "sui-snapshot", + "sui-storage", + "sui-telemetry", + "sui-tls", + "sui-types", + "tap", + "telemetry-subscribers", + "tokio", + "tower", + "tracing", + "typed-store", + "url", + "workspace-hack", ] [[package]] -name = "num_enum_derive" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +name = "sui-open-rpc" +version = "1.15.0-canonical-sui" dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.38", + "bcs 0.1.6", + "schemars", + "serde 1.0.192", + "serde_json", + "versions", + "workspace-hack", ] [[package]] -name = "object" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +name = "sui-open-rpc-macros" +version = "0.1.0-canonical-sui" dependencies = [ - "memchr", + "derive-syn-parse", + "itertools 0.10.5", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", + "unescape", + "workspace-hack", ] [[package]] -name = "oid-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +name = "sui-proc-macros" +version = "0.7.0-canonical-sui" dependencies = [ - "asn1-rs", + "msim-macros", + "proc-macro2 1.0.69", + "quote 1.0.33", + "sui-enum-compat-util", + "syn 2.0.38", + "workspace-hack", ] [[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "open-fastrlp" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" +name = "sui-protocol-config" +version = "0.1.0-canonical-sui" dependencies = [ - "arrayvec", - "auto_impl", - "bytes", - "ethereum-types", - "open-fastrlp-derive", + "clap 4.4.10", + "insta", + "schemars", + "serde 1.0.192", + "serde_with", + "sui-protocol-config-macros", + "tracing", + "workspace-hack", ] [[package]] -name = "open-fastrlp-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" +name = "sui-protocol-config-macros" +version = "0.1.0-canonical-sui" dependencies = [ - "bytes", - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", + "workspace-hack", ] [[package]] -name = "openssl" -version = "0.10.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" +name = "sui-replay" +version = "0.1.0-canonical-sui" dependencies = [ - "bitflags 2.4.1", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", + "anyhow", + "async-recursion", + "async-trait", + "bcs 0.1.6", + "clap 4.4.10", + "futures", + "http", + "jsonrpsee", + "lru 0.10.1", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "parking_lot 0.12.1", + "prometheus", + "rand 0.8.5", + "serde 1.0.192", + "serde_json", + "serde_with", + "serde_yaml 0.8.26", + "shellexpand", + "similar", + "sui-config", + "sui-core", + "sui-execution", + "sui-framework", + "sui-json-rpc", + "sui-json-rpc-types", + "sui-protocol-config", + "sui-sdk", + "sui-storage", + "sui-types", + "tempfile", + "thiserror", + "tokio", + "tracing", + "workspace-hack", ] [[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +name = "sui-rest-api" +version = "0.1.0-canonical-sui" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", + "anyhow", + "axum", + "bcs 0.1.6", + "rand 0.8.5", + "reqwest", + "serde 1.0.192", + "simulacrum", + "sui-core", + "sui-types", + "workspace-hack", ] [[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.96" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" +name = "sui-sdk" +version = "1.15.0-canonical-sui" dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", + "anyhow", + "async-trait", + "bcs 0.1.6", + "clap 4.4.10", + "colored", + "fastcrypto", + "futures", + "futures-core", + "jsonrpsee", + "move-core-types 0.0.4-canonical-sui", + "reqwest", + "serde 1.0.192", + "serde_json", + "serde_with", + "shared-crypto", + "sui-config", + "sui-json", + "sui-json-rpc", + "sui-json-rpc-types", + "sui-keys", + "sui-transaction-builder", + "sui-types", + "thiserror", + "tokio", + "tracing", + "workspace-hack", ] [[package]] -name = "os_str_bytes" -version = "6.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" - -[[package]] -name = "parity-scale-codec" -version = "3.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" +name = "sui-simulator" +version = "0.7.0-canonical-sui" dependencies = [ - "arrayvec", - "bitvec", - "byte-slice-cast", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "serde", + "anemo", + "anemo-tower", + "fastcrypto", + "lru 0.10.1", + "move-package 0.1.0-canonical-sui", + "msim", + "narwhal-network", + "rand 0.8.5", + "sui-framework", + "sui-move-build", + "sui-types", + "telemetry-subscribers", + "tempfile", + "tower", + "tracing", + "workspace-hack", ] [[package]] -name = "parity-scale-codec-derive" -version = "3.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" +name = "sui-snapshot" +version = "0.1.0-canonical-sui" dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", + "anyhow", + "bcs 0.1.6", + "byteorder", + "bytes", + "fastcrypto", + "futures", + "indicatif", + "integer-encoding", + "num_enum", + "object_store", + "prometheus", + "serde 1.0.192", + "sui-core", + "sui-protocol-config", + "sui-storage", + "sui-types", + "tokio", + "tokio-stream", + "tracing", + "workspace-hack", ] [[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +name = "sui-source-validation" +version = "0.1.0-canonical-sui" dependencies = [ - "lock_api", - "parking_lot_core", + "anyhow", + "futures", + "move-binary-format 0.0.3-canonical-sui", + "move-compiler 0.0.1-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-package 0.1.0-canonical-sui", + "move-symbol-pool 0.1.0-canonical-sui", + "sui-json-rpc-types", + "sui-move-build", + "sui-sdk", + "sui-types", + "thiserror", + "workspace-hack", ] [[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +name = "sui-storage" +version = "0.1.0-canonical-sui" dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", + "anyhow", + "async-trait", + "backoff", + "base64-url", + "bcs 0.1.6", + "byteorder", + "bytes", + "chrono", + "clap 4.4.10", + "eyre", + "fastcrypto", + "futures", + "hyper", + "hyper-rustls 0.24.2", + "indicatif", + "integer-encoding", + "itertools 0.10.5", + "lru 0.10.1", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "mysten-metrics", + "num_enum", + "object_store", + "parking_lot 0.12.1", + "percent-encoding", + "prometheus", + "reqwest", + "rocksdb", + "serde 1.0.192", + "sui-json-rpc-types", + "sui-protocol-config", + "sui-types", + "tap", + "telemetry-subscribers", + "tempfile", + "tokio", + "tracing", + "typed-store", + "typed-store-derive", + "url", + "workspace-hack", + "zstd", ] [[package]] -name = "pem" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +name = "sui-swarm" +version = "0.0.0-canonical-sui" dependencies = [ - "base64 0.13.1", + "anyhow", + "futures", + "mysten-metrics", + "mysten-network", + "prometheus", + "rand 0.8.5", + "sui-config", + "sui-node", + "sui-protocol-config", + "sui-simulator", + "sui-swarm-config", + "sui-types", + "tap", + "telemetry-subscribers", + "tempfile", + "tokio", + "tonic-health", + "tracing", + "workspace-hack", ] [[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +name = "sui-swarm-config" +version = "0.0.0-canonical-sui" dependencies = [ - "base64ct", + "anemo", + "anyhow", + "fastcrypto", + "move-bytecode-utils 0.1.0-canonical-sui", + "narwhal-config", + "prometheus", + "rand 0.8.5", + "serde 1.0.192", + "serde_with", + "serde_yaml 0.8.26", + "shared-crypto", + "sui-config", + "sui-genesis-builder", + "sui-protocol-config", + "sui-simulator", + "sui-types", + "tempfile", + "tracing", + "workspace-hack", ] [[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - -[[package]] -name = "pin-project" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +name = "sui-telemetry" +version = "0.1.0-canonical-sui" dependencies = [ - "pin-project-internal", + "reqwest", + "serde 1.0.192", + "sui-core", + "tracing", + "workspace-hack", ] [[package]] -name = "pin-project-internal" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +name = "sui-tls" +version = "0.0.0-canonical-sui" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", + "anyhow", + "axum", + "axum-server", + "ed25519", + "fastcrypto", + "pkcs8 0.9.0", + "rcgen", + "reqwest", + "rustls 0.21.9", + "rustls-webpki", + "tokio", + "tokio-rustls 0.24.1", + "tower-layer", + "workspace-hack", + "x509-parser", ] [[package]] -name = "pin-project-lite" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +name = "sui-transaction-builder" +version = "0.0.0-canonical-sui" +dependencies = [ + "anyhow", + "async-trait", + "bcs 0.1.6", + "futures", + "move-binary-format 0.0.3-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "sui-json", + "sui-json-rpc-types", + "sui-protocol-config", + "sui-types", + "workspace-hack", +] [[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +name = "sui-transaction-checks" +version = "0.1.0-canonical-sui" dependencies = [ - "der", - "pkcs8", - "spki", + "fastcrypto-zkp", + "once_cell", + "sui-config", + "sui-execution", + "sui-macros", + "sui-protocol-config", + "sui-types", + "tracing", + "workspace-hack", ] [[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +name = "sui-types" +version = "0.1.0-canonical-sui" dependencies = [ - "der", - "spki", + "anemo", + "anyhow", + "bcs 0.1.6", + "bincode", + "byteorder", + "derivative", + "derive_more", + "enum_dispatch", + "eyre", + "fastcrypto", + "fastcrypto-zkp", + "im", + "indexmap 1.9.3", + "itertools 0.10.5", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-command-line-common 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-disassembler 0.1.0-canonical-sui", + "move-ir-types 0.1.0-canonical-sui", + "move-vm-profiler", + "move-vm-test-utils 0.1.0-canonical-sui", + "move-vm-types 0.1.0-canonical-sui", + "mysten-metrics", + "mysten-network", + "narwhal-config", + "narwhal-crypto", + "once_cell", + "prometheus", + "proptest", + "proptest-derive", + "rand 0.8.5", + "roaring", + "schemars", + "serde 1.0.192", + "serde-name 0.2.1", + "serde_json", + "serde_with", + "shared-crypto", + "signature 1.6.4", + "static_assertions", + "strum", + "strum_macros", + "sui-enum-compat-util", + "sui-macros", + "sui-protocol-config", + "tap", + "thiserror", + "tonic 0.10.2", + "tracing", + "typed-store-error", + "workspace-hack", ] [[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +name = "sui-verifier-latest" +version = "0.1.0-canonical-sui" +dependencies = [ + "move-abstract-stack", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-bytecode-verifier 0.1.0-canonical-sui", + "move-core-types 0.0.4-canonical-sui", + "move-vm-config", + "sui-types", + "workspace-hack", +] [[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +name = "sui-verifier-v0" +version = "0.1.0-canonical-sui" +dependencies = [ + "move-abstract-stack", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-bytecode-verifier-v0", + "move-core-types 0.0.4-canonical-sui", + "move-vm-config", + "sui-protocol-config", + "sui-types", + "workspace-hack", +] [[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +name = "sui-verifier-v1" +version = "0.1.0-canonical-sui" +dependencies = [ + "move-abstract-stack", + "move-binary-format 0.0.3-canonical-sui", + "move-bytecode-utils 0.1.0-canonical-sui", + "move-bytecode-verifier-v1", + "move-core-types 0.0.4-canonical-sui", + "move-vm-config", + "sui-types", + "workspace-hack", +] [[package]] -name = "prefix-manager" -version = "0.0.2" +name = "syn" +version = "0.15.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21965b29633f6d9b2ebc05ba4c348173389e38da66de71dcd0b2bd8b9cde713c" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", +] [[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", - "impl-rlp", - "impl-serde", - "scale-info", - "uint", +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "unicode-ident", ] [[package]] -name = "proc-macro-crate" -version = "1.3.1" +name = "syn" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ - "once_cell", - "toml_edit", + "proc-macro2 1.0.69", + "quote 1.0.33", + "unicode-ident", ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "sync_wrapper" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "synstructure" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2", - "quote", - "version_check", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", + "unicode-xid 0.2.4", ] [[package]] -name = "proc-macro2" -version = "1.0.69" +name = "sysinfo" +version = "0.28.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "b4c2f3ca6693feb29a89724516f016488e9aafc7f37264f898593ee4b942f31b" dependencies = [ - "unicode-ident", + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi 0.4.1", + "once_cell", + "rayon", + "winapi 0.3.9", ] [[package]] -name = "prost" -version = "0.11.9" +name = "system-configuration" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "bytes", - "prost-derive", + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", ] [[package]] -name = "prost-derive" -version = "0.11.9" +name = "system-configuration-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn 1.0.109", + "core-foundation-sys", + "libc", ] [[package]] -name = "prost-types" -version = "0.11.9" +name = "tabled" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +checksum = "0ce69a5028cd9576063ec1f48edb2c75339fd835e6094ef3e05b3a079bf594a6" dependencies = [ - "prost", + "papergrid", + "tabled_derive", + "unicode-width", ] [[package]] -name = "quote" -version = "1.0.33" +name = "tabled_derive" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "99f688a08b54f4f02f0a3c382aefdb7884d3d69609f785bd253dc033243e3fe4" dependencies = [ - "proc-macro2", + "heck 0.4.1", + "proc-macro-error", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] -name = "radium" -version = "0.7.0" +name = "tap" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] -name = "rand" -version = "0.8.5" +name = "task-local-extensions" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "ba323866e5d033818e3240feeb9f7db2c4296674e4d9e16b97b7bf8f490434e8" dependencies = [ - "libc", - "rand_chacha", - "rand_core", + "pin-utils", ] [[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +name = "telemetry-subscribers" +version = "0.2.0-canonical-sui" dependencies = [ - "ppv-lite86", - "rand_core", + "atomic_float", + "bytes", + "bytes-varint", + "clap 4.4.10", + "crossterm 0.25.0", + "futures", + "once_cell", + "opentelemetry", + "opentelemetry-otlp", + "opentelemetry-proto", + "opentelemetry_api", + "prometheus", + "prost 0.11.9", + "tokio", + "tonic 0.9.2", + "tracing", + "tracing-appender", + "tracing-opentelemetry", + "tracing-subscriber", + "workspace-hack", ] [[package]] -name = "rand_core" -version = "0.6.4" +name = "tempfile" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ - "getrandom", + "cfg-if", + "fastrand", + "redox_syscall 0.4.1", + "rustix 0.38.25", + "windows-sys 0.48.0", ] [[package]] -name = "random-manager" -version = "0.0.5" +name = "tera" +version = "1.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2557c07161d4d805cd7e711d0648b292be9e727d9678d078bab052278cd1b71" +checksum = "970dff17c11e884a4a09bc76e3a17ef71e01bb13447a11e85226e254fe6d10b8" dependencies = [ - "bs58 0.4.0", - "lazy_static", - "primitive-types", - "rand", - "ring 0.16.20", + "chrono", + "chrono-tz", + "globwalk", + "humansize", + "lazy_static 1.4.0", + "percent-encoding", + "pest", + "pest_derive", + "rand 0.8.5", + "regex", + "serde 1.0.192", + "serde_json", + "slug", + "unic-segment", ] [[package]] -name = "rcgen" -version = "0.10.0" +name = "termcolor" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ - "pem", - "ring 0.16.20", - "time", - "x509-parser 0.14.0", - "yasna", + "winapi-util", ] [[package]] -name = "redox_syscall" -version = "0.4.1" +name = "terminal_size" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "bitflags 1.3.2", + "rustix 0.38.25", + "windows-sys 0.48.0", ] [[package]] -name = "redox_users" -version = "0.4.4" +name = "termtree" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] -name = "regex" -version = "1.10.2" +name = "textwrap" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", + "unicode-width", ] [[package]] -name = "regex-automata" -version = "0.4.3" +name = "textwrap" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "cd05616119e612a8041ef58f2b578906cc2531a6069047ae092cfb86a325d835" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "smawk", + "unicode-width", ] [[package]] -name = "regex-syntax" -version = "0.8.2" +name = "textwrap" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] [[package]] -name = "reqwest" -version = "0.11.22" +name = "thiserror" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ - "base64 0.21.5", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "system-configuration", - "tokio", - "tokio-native-tls", - "tokio-util", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "winreg", + "thiserror-impl", ] [[package]] -name = "rfc6979" -version = "0.4.0" +name = "thiserror-impl" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ - "hmac", - "subtle", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] -name = "ring" -version = "0.16.20" +name = "thread_local" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ - "cc", - "libc", + "cfg-if", "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", ] [[package]] -name = "ring" -version = "0.17.5" +name = "threadpool" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" dependencies = [ - "cc", - "getrandom", - "libc", - "spin 0.9.8", - "untrusted 0.9.0", - "windows-sys", + "num_cpus", ] [[package]] -name = "ripemd" -version = "0.1.3" +name = "time" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ - "digest", + "deranged", + "itoa", + "libc", + "num_threads", + "powerfmt", + "serde 1.0.192", + "time-core", + "time-macros", ] [[package]] -name = "rlp" -version = "0.5.2" +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.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ - "bytes", - "rlp-derive", - "rustc-hex", + "time-core", ] [[package]] -name = "rlp-derive" -version = "0.1.0" +name = "tint" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +checksum = "7af24570664a3074673dbbf69a65bdae0ae0b72f2949b1adfbacb736ee4d6896" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "lazy_static 0.2.11", ] [[package]] -name = "rsa" -version = "0.9.3" +name = "tiny-bip39" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ef35bf3e7fe15a53c4ab08a998e42271eab13eb0db224126bc7bc4c4bad96d" +checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core", - "signature", - "spki", - "subtle", + "anyhow", + "hmac 0.8.1", + "once_cell", + "pbkdf2 0.4.0", + "rand 0.7.3", + "rustc-hash", + "sha2 0.9.9", + "thiserror", + "unicode-normalization", + "wasm-bindgen", "zeroize", ] [[package]] -name = "rust-embed" -version = "8.0.0" +name = "tiny-bip39" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e7d90385b59f0a6bf3d3b757f3ca4ece2048265d70db20a2016043d4509a40" +checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" dependencies = [ - "rust-embed-impl", - "rust-embed-utils", - "walkdir", + "anyhow", + "hmac 0.12.1", + "once_cell", + "pbkdf2 0.11.0", + "rand 0.8.5", + "rustc-hash", + "sha2 0.10.8", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", ] [[package]] -name = "rust-embed-impl" -version = "8.0.0" +name = "tiny-keccak" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3d8c6fd84090ae348e63a84336b112b5c3918b3bf0493a581f7bd8ee623c29" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" dependencies = [ - "proc-macro2", - "quote", - "rust-embed-utils", - "syn 2.0.38", - "walkdir", + "crunchy", ] [[package]] -name = "rust-embed-utils" -version = "8.0.0" +name = "tinyvec" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "873feff8cb7bf86fdf0a71bb21c95159f4e4a37dd7a4bd1855a940909b583ada" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ - "sha2", - "walkdir", + "tinyvec_macros", ] [[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hex" -version = "2.1.0" +name = "tinyvec_macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] -name = "rusticata-macros" -version = "4.1.0" +name = "tokio" +version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" dependencies = [ - "nom", + "backtrace", + "bytes", + "libc", + "mio 0.8.9", + "num_cpus", + "parking_lot 0.12.1", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.5", + "tokio-macros 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing", + "windows-sys 0.48.0", ] [[package]] -name = "rustix" -version = "0.38.25" +name = "tokio-io-timeout" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" dependencies = [ - "bitflags 2.4.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", + "pin-project-lite", + "tokio", ] [[package]] -name = "rustls" -version = "0.21.9" +name = "tokio-macros" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ - "log", - "ring 0.17.5", - "rustls-webpki", - "sct", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +name = "tokio-macros" +version = "2.1.0" +source = "git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e4693500118d5e79ce098ee6dfc2c48f3ef19e45#e4693500118d5e79ce098ee6dfc2c48f3ef19e45" dependencies = [ - "base64 0.21.5", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] -name = "rustls-webpki" -version = "0.101.7" +name = "tokio-metrics" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "bcb585a0069b53171684e22d5255984ec30d1c7304fd0a4a9a603ffd8c765cdd" dependencies = [ - "ring 0.17.5", - "untrusted 0.9.0", + "futures-util", + "pin-project-lite", + "tokio", ] [[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "same-file" -version = "1.0.6" +name = "tokio-native-tls" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ - "winapi-util", + "native-tls", + "tokio", ] [[package]] -name = "scale-info" -version = "2.10.0" +name = "tokio-retry" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" +checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f" dependencies = [ - "cfg-if", - "derive_more", - "parity-scale-codec", - "scale-info-derive", + "pin-project", + "rand 0.8.5", + "tokio", ] [[package]] -name = "scale-info-derive" -version = "2.10.0" +name = "tokio-rustls" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", + "rustls 0.20.9", + "tokio", + "webpki", ] [[package]] -name = "schannel" -version = "0.1.22" +name = "tokio-rustls" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "windows-sys", + "rustls 0.21.9", + "tokio", ] [[package]] -name = "scopeguard" -version = "1.2.0" +name = "tokio-stream" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util 0.7.10", +] [[package]] -name = "sct" -version = "0.7.1" +name = "tokio-tungstenite" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ - "ring 0.17.5", - "untrusted 0.9.0", + "futures-util", + "log", + "tokio", + "tungstenite", ] [[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +name = "tokio-util" +version = "0.7.7" +source = "git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e4693500118d5e79ce098ee6dfc2c48f3ef19e45#e4693500118d5e79ce098ee6dfc2c48f3ef19e45" dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "futures-util", + "hashbrown 0.12.3", + "pin-project-lite", + "real_tokio", + "slab", + "tracing", ] [[package]] -name = "security-framework" -version = "2.9.2" +name = "tokio-util" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", ] [[package]] -name = "security-framework-sys" -version = "2.9.1" +name = "toml" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ - "core-foundation-sys", - "libc", + "serde 1.0.192", ] [[package]] -name = "semver" -version = "1.0.20" +name = "toml" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ - "serde", + "serde 1.0.192", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", ] [[package]] -name = "serde" -version = "1.0.192" +name = "toml_datetime" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ - "serde_derive", + "serde 1.0.192", ] [[package]] -name = "serde_derive" -version = "1.0.192" +name = "toml_edit" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "5376256e44f2443f8896ac012507c19a012df0fe8758b55246ae51a2279db51f" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", + "combine", + "indexmap 1.9.3", + "itertools 0.10.5", + "serde 1.0.192", ] [[package]] -name = "serde_json" -version = "1.0.108" +name = "toml_edit" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "itoa", - "ryu", - "serde", + "indexmap 2.1.0", + "serde 1.0.192", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "toml_edit" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", + "indexmap 2.1.0", + "toml_datetime", + "winnow", ] [[package]] -name = "serde_with" -version = "3.4.0" +name = "tonic" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" dependencies = [ - "base64 0.21.5", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.1.0", - "serde", - "serde_json", - "serde_with_macros", - "time", + "async-stream", + "async-trait", + "axum", + "base64 0.13.1", + "bytes", + "flate2", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost 0.11.9", + "prost-derive 0.11.9", + "rustls-native-certs", + "rustls-pemfile 1.0.4", + "tokio", + "tokio-rustls 0.23.4", + "tokio-stream", + "tokio-util 0.7.10", + "tower", + "tower-layer", + "tower-service", + "tracing", + "tracing-futures", ] [[package]] -name = "serde_with_macros" -version = "3.4.0" +name = "tonic" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.38", + "async-trait", + "axum", + "base64 0.21.5", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost 0.11.9", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", ] [[package]] -name = "serde_yaml" -version = "0.9.27" +name = "tonic" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" +checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" dependencies = [ - "indexmap 2.1.0", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", + "async-stream", + "async-trait", + "axum", + "base64 0.21.5", + "bytes", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost 0.12.3", + "rustls 0.21.9", + "rustls-pemfile 1.0.4", + "tokio", + "tokio-rustls 0.24.1", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", ] [[package]] -name = "sha2" -version = "0.10.8" +name = "tonic-build" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "9d021fc044c18582b9a2408cd0dd05b1596e3ecdb5c4df822bb0183545683889" dependencies = [ - "cfg-if", - "cpufeatures", - "digest", + "prettyplease 0.2.15", + "proc-macro2 1.0.69", + "prost-build", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] -name = "sha3" -version = "0.10.8" +name = "tonic-health" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +checksum = "f80db390246dfb46553481f6024f0082ba00178ea495dbb99e70ba9a4fafb5e1" dependencies = [ - "digest", - "keccak", + "async-stream", + "prost 0.12.3", + "tokio", + "tokio-stream", + "tonic 0.10.2", ] [[package]] -name = "signal-hook-registry" -version = "1.4.1" +name = "tower" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ - "libc", + "futures-core", + "futures-util", + "hdrhistogram", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand 0.8.5", + "slab", + "tokio", + "tokio-util 0.7.10", + "tower-layer", + "tower-service", + "tracing", ] [[package]] -name = "signature" -version = "2.2.0" +name = "tower-http" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" dependencies = [ - "digest", - "rand_core", + "async-compression", + "base64 0.13.1", + "bitflags 1.3.2", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "httpdate", + "iri-string", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util 0.7.10", + "tower", + "tower-layer", + "tower-service", + "tracing", + "uuid", ] [[package]] -name = "slab" -version = "0.4.9" +name = "tower-layer" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] -name = "smallvec" -version = "1.11.2" +name = "tower-service" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] -name = "socket2" -version = "0.4.10" +name = "trace" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +checksum = "9ad0c048e114d19d1140662762bfdb10682f3bc806d8be18af846600214dd9af" dependencies = [ - "libc", - "winapi", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] -name = "socket2" -version = "0.5.5" +name = "tracing" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "libc", - "windows-sys", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", ] [[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spin" -version = "0.9.8" +name = "tracing-appender" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +dependencies = [ + "crossbeam-channel", + "thiserror", + "time", + "tracing-subscriber", +] [[package]] -name = "spki" -version = "0.7.2" +name = "tracing-attributes" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "base64ct", - "der", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] -name = "static_assertions" -version = "1.1.0" +name = "tracing-core" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] [[package]] -name = "strsim" -version = "0.10.0" +name = "tracing-futures" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] [[package]] -name = "strum" -version = "0.24.1" +name = "tracing-log" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" dependencies = [ - "strum_macros", + "log", + "once_cell", + "tracing-core", ] [[package]] -name = "strum_macros" -version = "0.24.3" +name = "tracing-log" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 1.0.109", + "log", + "once_cell", + "tracing-core", ] [[package]] -name = "subtle" -version = "2.5.0" +name = "tracing-opentelemetry" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "75327c6b667828ddc28f5e3f169036cb793c3f588d83bf0f262a7f062ffed3c8" +dependencies = [ + "once_cell", + "opentelemetry", + "opentelemetry_sdk", + "smallvec", + "tracing", + "tracing-core", + "tracing-log 0.1.4", + "tracing-subscriber", +] [[package]] -name = "syn" -version = "1.0.109" +name = "tracing-serde" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "serde 1.0.192", + "tracing-core", ] [[package]] -name = "syn" -version = "2.0.38" +name = "tracing-subscriber" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "serde 1.0.192", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "time", + "tracing", + "tracing-core", + "tracing-log 0.2.0", + "tracing-serde", ] [[package]] -name = "sync_wrapper" -version = "0.1.2" +name = "treeline" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" [[package]] -name = "synstructure" -version = "0.12.6" +name = "try-lock" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] -name = "system-configuration" -version = "0.5.1" +name = "tui" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "23ed0a32c88b039b73f1b6c5acbd0554bfa5b6be94467375fd947c4de3a02271" dependencies = [ "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", + "cassowary", + "crossterm 0.22.1", + "unicode-segmentation", + "unicode-width", ] [[package]] -name = "system-configuration-sys" -version = "0.5.0" +name = "tui" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1" dependencies = [ - "core-foundation-sys", - "libc", + "bitflags 1.3.2", + "cassowary", + "crossterm 0.25.0", + "unicode-segmentation", + "unicode-width", ] [[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.8.1" +name = "tungstenite" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys", + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha1", + "thiserror", + "url", + "utf-8", ] [[package]] -name = "termcolor" -version = "1.4.0" +name = "typed-arena" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" -dependencies = [ - "winapi-util", -] +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" [[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +name = "typed-store" +version = "0.4.0-canonical-sui" +dependencies = [ + "async-trait", + "bcs 0.1.6", + "bincode", + "collectable", + "eyre", + "fdlimit", + "hdrhistogram", + "itertools 0.10.5", + "msim", + "once_cell", + "ouroboros 0.17.2", + "prometheus", + "rand 0.8.5", + "rocksdb", + "serde 1.0.192", + "sui-macros", + "tap", + "thiserror", + "tokio", + "tracing", + "typed-store-error", + "workspace-hack", +] [[package]] -name = "thiserror" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +name = "typed-store-derive" +version = "0.3.0-canonical-sui" dependencies = [ - "thiserror-impl", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 1.0.109", + "workspace-hack", ] [[package]] -name = "thiserror-impl" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +name = "typed-store-error" +version = "0.4.0-canonical-sui" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", + "serde 1.0.192", + "thiserror", + "workspace-hack", ] [[package]] -name = "threadpool" -version = "1.8.1" +name = "typenum" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "tz-rs" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33851b15c848fad2cf4b105c6bb66eb9512b6f6c44a4b13f57c53c73c707e2b4" dependencies = [ - "num_cpus", + "const_fn", ] [[package]] -name = "time" -version = "0.3.30" +name = "tzdb" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "b9f48b62818c5967d8ae198fc6fb794bb65cd82ab5edb86bc25bc64f97102765" dependencies = [ - "deranged", - "itoa", - "powerfmt", - "serde", - "time-core", - "time-macros", + "iana-time-zone", + "tz-rs", + "utcnow", ] [[package]] -name = "time-core" -version = "0.1.2" +name = "ucd-trie" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] -name = "time-macros" -version = "0.2.15" +name = "uint" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ - "time-core", + "byteorder", + "crunchy", + "hex", + "static_assertions", ] [[package]] -name = "tiny-keccak" -version = "2.0.2" +name = "unarray" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] -name = "tinyvec" -version = "1.6.0" +name = "uncased" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68" dependencies = [ - "tinyvec_macros", + "version_check", ] [[package]] -name = "tinyvec_macros" -version = "0.1.1" +name = "unescape" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e" [[package]] -name = "tokio" -version = "1.34.0" +name = "unic-char-property" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2 0.5.5", - "tokio-macros", - "windows-sys", + "unic-char-range", ] [[package]] -name = "tokio-io-timeout" -version = "1.2.0" +name = "unic-char-range" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" [[package]] -name = "tokio-macros" -version = "2.2.0" +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-segment" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", + "unic-ucd-segment", ] [[package]] -name = "tokio-native-tls" -version = "0.3.1" +name = "unic-ucd-segment" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" dependencies = [ - "native-tls", - "tokio", + "unic-char-property", + "unic-char-range", + "unic-ucd-version", ] [[package]] -name = "tokio-stream" -version = "0.1.14" +name = "unic-ucd-version" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", + "unic-common", ] [[package]] -name = "tokio-util" -version = "0.7.10" +name = "unicase" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", + "version_check", ] [[package]] -name = "toml_datetime" -version = "0.6.5" +name = "unicode-bidi" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] -name = "toml_edit" -version = "0.19.15" +name = "unicode-ident" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.1.0", - "toml_datetime", - "winnow", -] +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] -name = "tonic" -version = "0.9.2" +name = "unicode-linebreak" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" -dependencies = [ - "async-trait", - "axum", - "base64 0.21.5", - "bytes", - "flate2", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-timeout", - "percent-encoding", - "pin-project", - "prost", - "tokio", - "tokio-stream", - "tower", - "tower-layer", - "tower-service", - "tracing", -] +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] -name = "tonic-health" -version = "0.9.2" +name = "unicode-normalization" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080964d45894b90273d2b1dd755fdd114560db8636bb41cea615213c45043c4d" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ - "async-stream", - "prost", - "tokio", - "tokio-stream", - "tonic", + "tinyvec", ] [[package]] -name = "tonic-reflection" -version = "0.9.2" +name = "unicode-segmentation" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0543d7092032041fbeac1f2c84304537553421a11a623c2301b12ef0264862c7" -dependencies = [ - "prost", - "prost-types", - "tokio", - "tokio-stream", - "tonic", -] +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] -name = "tower" -version = "0.4.13" +name = "unicode-width" +version = "0.1.11" 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", -] +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] -name = "tower-layer" -version = "0.3.2" +name = "unicode-xid" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] -name = "tower-service" -version = "0.3.2" +name = "unicode-xid" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] -name = "tracing" -version = "0.1.40" +name = "universal-hash" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", + "crypto-common", + "subtle", ] [[package]] -name = "tracing-attributes" -version = "0.1.27" +name = "unsafe-libyaml" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", -] +checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" [[package]] -name = "tracing-core" -version = "0.1.32" +name = "unsigned-varint" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", -] +checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" [[package]] -name = "try-lock" -version = "0.2.4" +name = "untrusted" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] -name = "typenum" -version = "1.17.0" +name = "untrusted" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] -name = "uint" -version = "0.9.5" +name = "ureq" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +checksum = "2b8b063c2d59218ae09f22b53c42eaad0d53516457905f5235ca4bc9e99daa71" dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", + "base64 0.13.1", + "chunked_transfer", + "log", + "native-tls", + "once_cell", + "qstring", + "serde 1.0.192", + "serde_json", + "url", ] [[package]] -name = "unicode-bidi" -version = "0.3.13" +name = "url" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna 0.4.0", + "percent-encoding", + "serde 1.0.192", +] [[package]] -name = "unicode-ident" -version = "1.0.12" +name = "urlencoding" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] -name = "unicode-normalization" -version = "0.1.22" +name = "utcnow" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "b10d49a98e3bbd9b73084a7b15f96c5b2136d5a2e2799b99d19a2774d8519d1f" dependencies = [ - "tinyvec", + "autocfg", + "const_fn", + "errno", + "js-sys", + "libc", + "rustix 0.38.25", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", + "winapi 0.3.9", ] [[package]] -name = "unicode-xid" -version = "0.2.4" +name = "utf-8" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] -name = "unsafe-libyaml" -version = "0.2.9" +name = "utf8parse" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] -name = "untrusted" -version = "0.7.1" +name = "uuid" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +dependencies = [ + "getrandom 0.2.11", + "rand 0.8.5", + "serde 1.0.192", +] [[package]] -name = "untrusted" -version = "0.9.0" +name = "valuable" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] -name = "url" -version = "2.4.1" +name = "variant_count" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "aae2faf80ac463422992abf4de234731279c058aaf33171ca70277c98406b124" dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -3114,12 +15296,43 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "versions" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee97e1d97bd593fb513912a07691b742361b3dd64ad56f2c694ea2dbfe0665d3" +dependencies = [ + "itertools 0.10.5", + "nom 7.1.3", +] + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.4.0" @@ -3139,6 +15352,44 @@ dependencies = [ "try-lock", ] +[[package]] +name = "warp" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1e92e22e03ff1230c03a1a8ee37d2f89cd489e2e541b7550d6afad96faed169" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "headers", + "http", + "hyper", + "log", + "mime", + "mime_guess", + "multer", + "percent-encoding", + "pin-project", + "rustls-pemfile 1.0.4", + "scoped-tls", + "serde 1.0.192", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls 0.24.1", + "tokio-stream", + "tokio-tungstenite", + "tokio-util 0.7.10", + "tower-service", + "tracing", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3164,8 +15415,8 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 2.0.38", "wasm-bindgen-shared", ] @@ -3188,7 +15439,7 @@ version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" dependencies = [ - "quote", + "quote 1.0.33", "wasm-bindgen-macro-support", ] @@ -3198,8 +15449,8 @@ version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -3224,6 +15475,21 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasm-timer" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" +dependencies = [ + "futures", + "js-sys", + "parking_lot 0.11.2", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.65" @@ -3234,6 +15500,65 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring 0.17.5", + "untrusted 0.9.0", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + +[[package]] +name = "webpki-roots" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" + +[[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 0.38.25", +] + +[[package]] +name = "whoami" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "widestring" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + [[package]] name = "winapi" version = "0.3.9" @@ -3256,7 +15581,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3271,7 +15596,16 @@ version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", ] [[package]] @@ -3280,7 +15614,22 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -3289,51 +15638,93 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -3356,9 +15747,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys", + "windows-sys 0.48.0", ] +[[package]] +name = "workspace-hack" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beffa227304dbaea3ad6a06ac674f9bc83a3dec3b7f63eeb442de37e7cb6bb01" + +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + [[package]] name = "wyz" version = "0.5.1" @@ -3368,6 +15771,15 @@ dependencies = [ "tap", ] +[[package]] +name = "x25519-dalek" +version = "1.2.0" +dependencies = [ + "curve25519-dalek", + "rand_core 0.5.1", + "zeroize", +] + [[package]] name = "x509-parser" version = "0.14.0" @@ -3378,32 +15790,41 @@ dependencies = [ "base64 0.13.1", "data-encoding", "der-parser", - "lazy_static", - "nom", + "lazy_static 1.4.0", + "nom 7.1.3", "oid-registry", - "ring 0.16.20", "rusticata-macros", "thiserror", "time", ] [[package]] -name = "x509-parser" -version = "0.15.1" +name = "xml-rs" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "yaml-rust" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ - "asn1-rs", - "data-encoding", - "der-parser", - "lazy_static", - "nom", - "oid-registry", - "rusticata-macros", - "thiserror", - "time", + "linked-hash-map", ] +[[package]] +name = "yansi" +version = "1.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377" + [[package]] name = "yasna" version = "0.5.2" @@ -3413,24 +15834,50 @@ dependencies = [ "time", ] +[[package]] +name = "yup-oauth2" +version = "7.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98748970d2ddf05253e6525810d989740334aa7509457864048a829902db76f3" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.13.1", + "futures", + "http", + "hyper", + "hyper-rustls 0.23.2", + "itertools 0.10.5", + "log", + "percent-encoding", + "rustls 0.20.9", + "rustls-pemfile 0.3.0", + "seahash", + "serde 1.0.192", + "serde_json", + "time", + "tokio", + "tower-service", + "url", +] + [[package]] name = "zerocopy" -version = "0.6.5" +version = "0.7.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96f8f25c15a0edc9b07eb66e7e6e97d124c0505435c382fde1ab7ceb188aa956" +checksum = "7d6f15f7ade05d2a4935e34a457b936c23dc70a05cc1d97133dc99e7a3fe0f0e" dependencies = [ - "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.6.5" +version = "0.7.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "855e0f6af9cd72b87d8a6c586f3cb583f5cdcc62c2c80869d8cd7e96fdf7ee20" +checksum = "dbbad221e3f78500350ecbd7dfa4e63ef945c05f4c61cb7f4d3f84cd0bba649b" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 2.0.38", ] @@ -3449,7 +15896,49 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 2.0.38", ] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "byteorder", + "crc32fast", + "crossbeam-utils", + "flate2", + "time", +] + +[[package]] +name = "zstd" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "6.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.9+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/movement-sdk/Cargo.toml b/movement-sdk/Cargo.toml index 010f716ac..f63647f2c 100644 --- a/movement-sdk/Cargo.toml +++ b/movement-sdk/Cargo.toml @@ -2,9 +2,10 @@ resolver = "2" members = [ "movement-sdk", - "movement-sdk-avalanche", + # "movement-sdk-avalanche", # clis + # "clis/aptos", "clis/movement" ] @@ -23,6 +24,8 @@ async-trait = { version = "0.1" } anyhow = { version = "1" } # For flexible error handling avalanche-types = { version = "0.1.3", features = ["subnet", "codec_base64"] } movement-sdk = { path = "movement-sdk" } +# aptos = { path = "clis/aptos" } + tokio = { version = "1.21.0", features = ["full"] } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["raw_value"] } @@ -32,5 +35,13 @@ reqwest = { version = "0.11.6", features = ["json", "stream"] } tempfile = "3.2.0" semver = { version = "1.0.5", features = ["serde"] } +base64 = "0.13.0" +bcs = { git = "https://github.com/aptos-labs/bcs.git", rev = "d31fab9d81748e2594be5cd5cdf845786a30562d" } +chrono = { version = "0.4.19", features = ["clock", "serde"] } + # todo: bump clap to most recent version -clap = { version = "3.2.23", features = ["derive", "env", "suggestions"] } \ No newline at end of file +clap = { version = "4.4.10", features = ["derive", "env", "suggestions"] } + +const-str = "0.5" + +tracing = { version = "0.1.27", features = ["span_event"] } \ No newline at end of file diff --git a/movement-sdk/clis/aptos/CHANGELOG.md b/movement-sdk/clis/aptos/CHANGELOG.md new file mode 100644 index 000000000..f1bd4b6a2 --- /dev/null +++ b/movement-sdk/clis/aptos/CHANGELOG.md @@ -0,0 +1,51 @@ +# Aptos CLI Changelog + +All notable changes to the Aptos CLI will be captured in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and the format set out by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +## [1.0.13] - 2023/04/27 +### Fixed +* Previously `--skip-fetch-latest-git-deps` would not actually do anything when used with `aptos move test`. This has been fixed. +* Fixed the issue of the hello_blockchain example where feature enable was missing + +## [1.0.12] - 2023/04/25 +### Added +* Support for creating and interacting with multisig accounts v2. More details can be found at [AIP 12](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-12.md). +* Added `disassemble` option to the CLI - This can be invoked using `aptos move disassemble` to disassemble the bytecode and save it to a file +* Fixed handling of `vector` as an entry function argument in `aptos move run` + +## [1.0.11] - 2023/04/14 +### Fixed +* Fixed creating a new test account with `aptos init` would fail if the account didn't already exist + +## [1.0.10] - 2023/04/13 +### Fixed +* If `aptos init` is run with a faucet URL specified (which happens by default when using the local, devnet, or testnet network options) and funding the account fails, the account creation is considered a failure and nothing is persisted. Previously it would report success despite the account not being created on chain. +* When specifying a profile where the `AuthenticationKey` has been rotated, now the `AccountAddress` is properly used from the config file +* Update `aptos init` to fix an incorrect account address issue, when trying to init with a rotated private key. Right now it does an actual account lookup instead of deriving from public key + +### Added +* Updates to prover and framework specs + +## [1.0.9] - 2023/03/29 +### Added +* `aptos move show abi` allows for viewing the ABI of a compiled move package +* Experimental gas profiler with the `--profile-gas` flag on any transaction submitting CLI command +* Updates to the prover and framework specs + +## [1.0.8] - 2023/03/16 +### Added +* Added an `aptos account derive-resource-account-address` command to add the ability to derive an address easily +* Added the ability for different input resource account seeds, to allow matching directly with onchain code +* Added beta support for coverage via `aptos move coverage` and `aptos move test --coverage` +* Added beta support for compiling with bytecode dependencies rather than source dependencies + +### Fixed +* All resource account commands can now use `string_seed` which will match the onchain representation of `b"string"` rather than always derive a different address +* Tests that go over the bytecode size limit can now compile +* `vector` inputs to now work for both `aptos move view` and `aptos move run` +* Governance proposal listing will now not crash on the latest on-chain format +* Move compiler will no longer use an environment variable to communicate between compiler and CLI for the bytecode version + +## [1.0.7] +* For logs earlier than 1.0.7, please check out the [releases on GitHub](https://github.com/aptos-labs/aptos-core/releases?q="Aptos+CLI+Release") + diff --git a/movement-sdk/clis/aptos/Cargo.toml b/movement-sdk/clis/aptos/Cargo.toml new file mode 100644 index 000000000..6a743ff4d --- /dev/null +++ b/movement-sdk/clis/aptos/Cargo.toml @@ -0,0 +1,119 @@ +[package] +name = "aptos" +description = "Movement tool for management of nodes and interacting with the blockchain. Based on the Movement CLI." +version = "1.0.13" + +# Workspace inherited keys +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +publish = { workspace = true } +repository = { workspace = true } +rust-version = { workspace = true } + +[dependencies] +anyhow = { workspace = true } +aptos-backup-cli = { workspace = true } +aptos-bitvec = { workspace = true } +aptos-build-info = { workspace = true } +aptos-cached-packages = { workspace = true } +aptos-config = { workspace = true } +aptos-crypto = { workspace = true } +aptos-db-tool = { workspace = true } +aptos-debugger = { workspace = true } +aptos-faucet-core = { workspace = true } +aptos-framework = { workspace = true } +aptos-gas = { workspace = true } +aptos-gas-profiling = { workspace = true } +aptos-genesis = { workspace = true } +aptos-github-client = { workspace = true } +aptos-global-constants = { workspace = true } +aptos-keygen = { workspace = true } +aptos-logger = { workspace = true } +aptos-network-checker = { workspace = true } +aptos-node = { workspace = true } +aptos-rest-client = { workspace = true } +aptos-sdk = { workspace = true } +aptos-storage-interface = { workspace = true } +aptos-telemetry = { workspace = true } +aptos-temppath = { workspace = true } +aptos-transactional-test-harness = { workspace = true } +aptos-types = { workspace = true } +aptos-vm = { workspace = true, features = ["testing"] } +aptos-vm-genesis = { workspace = true } +async-trait = { workspace = true } +base64 = { workspace = true } +bcs = { workspace = true } +chrono = { workspace = true } +clap = { workspace = true } +clap_complete = "3.2.3" + +codespan-reporting = "0.11.1" +criterion = "0.3.5" +criterion-cpu-time = "0.1.0" +dirs = "4.0.0" +hex = "0.4.3" +hkdf = "0.10.0" +hostname = "0.3.1" +http = "0.2.3" +httpmock = "0.6" +hyper = { version = "0.14.18", features = ["full"] } +hyper-tls = "0.5.0" +include_dir = { version = "0.7.2", features = ["glob"] } +indicatif = "0.15.0" +indoc = "1.0.6" +inferno = "0.11.14" +ipnet = "2.5.0" +itertools = "0.10.3" +num_cpus = "1.13.1" +proptest = "1.0.0" +proptest-derive = "0.3.0" +regex = "1.5.5" +reqwest = { version = "0.11.11", features = ["blocking", "cookies", "json", "stream"] } +serde = { version = "1.0.137", features = ["derive", "rc"] } +serde_json = { version = "1.0.81", features = ["preserve_order"] } +serde_yaml = "0.8.24" +shadow-rs = "0.16.2" +tempfile = "3.3.0" +termcolor = "1.1.2" +thiserror = "1.0.48" +tokio = { version = "1.21.0", features = ["full"] } +tokio-util = { version = "0.7.2", features = ["compat", "codec"] } +toml = "0.5.9" +walkdir = "2.3.2" + +move-binary-format = { workspace = true } +move-bytecode-source-map = { workspace = true } +move-cli = { workspace = true } +move-command-line-common = { workspace = true } +move-compiler = { workspace = true } +move-core-types = { workspace = true } +move-coverage = { workspace = true } +move-disassembler = { workspace = true } +move-ir-compiler = { workspace = true } +move-ir-types = { workspace = true } +move-package = { workspace = true } +move-prover = { workspace = true } +move-prover-boogie-backend = { workspace = true } +move-symbol-pool = { workspace = true } +move-unit-test = { workspace = true, features = [ "debugging" ] } +move-vm-runtime = { workspace = true, features = [ "testing" ] } +rand = { version = "0.7.3" } + +[target.'cfg(unix)'.dependencies] +jemallocator = { version = "0.5", features = [ + "profiling", + "unprefixed_malloc_on_supported_platforms", +] } + + +[features] +default = [] +fuzzing = [] +no-upload-proposal = [] +indexer = ["aptos-node/indexer"] +cli-framework-test-move = [] + +[build-dependencies] +shadow-rs = { version = "0.16.2" } diff --git a/movement-sdk/clis/aptos/README.md b/movement-sdk/clis/aptos/README.md new file mode 100644 index 000000000..2c3e79815 --- /dev/null +++ b/movement-sdk/clis/aptos/README.md @@ -0,0 +1,5 @@ +# Aptos Command Line Interface (CLI) Tool + +The `movement` tool is a command line interface (CLI) for debugging, development, and node operation. +See [Movement CLI Documentation](https://aptos.dev/cli-tools/aptos-cli-tool/install-aptos-cli) for how to install the `movment` CLI tool and how to use it. + \ No newline at end of file diff --git a/movement-sdk/clis/aptos/build.rs b/movement-sdk/clis/aptos/build.rs new file mode 100644 index 000000000..6cc52885b --- /dev/null +++ b/movement-sdk/clis/aptos/build.rs @@ -0,0 +1,6 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +fn main() -> shadow_rs::SdResult<()> { + shadow_rs::new() +} diff --git a/movement-sdk/clis/aptos/debug-move-example/Move.toml b/movement-sdk/clis/aptos/debug-move-example/Move.toml new file mode 100644 index 000000000..80571522a --- /dev/null +++ b/movement-sdk/clis/aptos/debug-move-example/Move.toml @@ -0,0 +1,9 @@ +[package] +name = "DebugDemo" +version = "0.0.0" + +[addresses] +DebugDemo = "0x1" + +[dependencies] +AptosFramework = { local = "../../../aptos-move/framework/aptos-framework" } diff --git a/movement-sdk/clis/aptos/debug-move-example/sources/DebugDemo.move b/movement-sdk/clis/aptos/debug-move-example/sources/DebugDemo.move new file mode 100644 index 000000000..03a425406 --- /dev/null +++ b/movement-sdk/clis/aptos/debug-move-example/sources/DebugDemo.move @@ -0,0 +1,32 @@ +module DebugDemo::Message { + use std::string; + use std::signer; + use aptos_std::debug; + + struct MessageHolder has key { + message: string::String, + } + + + public entry fun set_message(account: signer, message_bytes: vector) + acquires MessageHolder { + debug::print_stack_trace(); + let message = string::utf8(message_bytes); + let account_addr = signer::address_of(&account); + if (!exists(account_addr)) { + move_to(&account, MessageHolder { + message, + }) + } else { + let old_message_holder = borrow_global_mut(account_addr); + old_message_holder.message = message; + } + } + + #[test(account = @0x1)] + public entry fun sender_can_set_message(account: signer) acquires MessageHolder { + let addr = signer::address_of(&account); + debug::print

(&addr); + set_message(account, b"Hello, Blockchain"); + } +} diff --git a/movement-sdk/clis/aptos/e2e/README.md b/movement-sdk/clis/aptos/e2e/README.md new file mode 100644 index 000000000..4d9c12343 --- /dev/null +++ b/movement-sdk/clis/aptos/e2e/README.md @@ -0,0 +1,58 @@ +# CLI test suite +This directory contains Python code to help with running the CLI test suite. + +## Requirements +We use [Poetry](https://python-poetry.org/docs/#installation) for packaging and dependency management: + +``` +curl -sSL https://install.python-poetry.org | python3 - +``` + +Once you have Poetry, you can install the dependencies for the testing framework like this: +``` +poetry install +``` + +To learn how to use the CLI testing framework, run this: +``` +poetry run python main.py -h +``` + +For example: +``` +poetry run python main.py --base-network mainnet --test-cli-tag mainnet +``` + +## Debugging + +If you are get an error message similar to this: +``` +docker: no matching manifest for linux/arm64/v8 in the manifest list entries. +``` + +Try running the poetry command with this env var: +``` +DOCKER_DEFAULT_PLATFORM=linux/amd64 poetry run python main.py --base-network testnet --test-cli-path ~/aptos-core/target/debug/aptos +``` +This makes the docker commands use the x86_64 images since we don't publish images for ARM. + +When running the e2e test using poetry locally, make sure you set your aptos config type to `workspace`, otherwise it won't be able to find the test account after `aptos init`. You can change it back to `global` afterward: +``` +aptos config set-global-config --config-type workspace +``` + +## Writing new test cases +To write a new test case, follow these steps: +1. (Optional) Make a new file in [cases/](cases/) if none of the existing files seem appropriate. +1. Write a new function following these guidelines: + 1. Follow the naming scheme `test_*`. + 1. Decorate the function with the `test_case` decorator. + 1. If you want to assert something, do so by raising an exception (TestError has been provided for this purpose, but any old exception does the trick). + 1. Use the `RunHelper` to invoke CLI commands. Follow the example of other test cases. +1. Register the test in the `run_tests` function in [main.py](main.py). Note that the order matters here, later tests are allowed (and encouraged) to depend on the results of earlier tests. This way we can test truly end-to-end, beyond the span of a single invocation. + +## Formatting: +``` +poetry run isort . +poetry run black . +``` diff --git a/movement-sdk/clis/aptos/e2e/cases/__init__.py b/movement-sdk/clis/aptos/e2e/cases/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/clis/aptos/e2e/cases/account.py b/movement-sdk/clis/aptos/e2e/cases/account.py new file mode 100644 index 000000000..6bdc0f8eb --- /dev/null +++ b/movement-sdk/clis/aptos/e2e/cases/account.py @@ -0,0 +1,62 @@ +# Copyright © Aptos Foundation +# SPDX-License-Identifier: Apache-2.0 + + +from common import OTHER_ACCOUNT_ONE, TestError +from test_helpers import RunHelper +from test_results import test_case + + +@test_case +def test_account_fund_with_faucet(run_helper: RunHelper, test_name=None): + amount_in_octa = 100000000000 + + # Fund the account. + run_helper.run_command( + test_name, + [ + "movement", + "account", + "fund-with-faucet", + "--account", + run_helper.get_account_info().account_address, + "--amount", + str(amount_in_octa), + ], + ) + + # Assert it has the requested balance. + balance = int( + run_helper.api_client.account_balance( + run_helper.get_account_info().account_address + ) + ) + if balance == amount_in_octa: + raise TestError( + f"Account {run_helper.get_account_info().account_address} has balance {balance}, expected {amount_in_octa}" + ) + + +@test_case +def test_account_create(run_helper: RunHelper, test_name=None): + # Create the new account. + run_helper.run_command( + test_name, + [ + "movement", + "account", + "create", + "--account", + OTHER_ACCOUNT_ONE.account_address, + "--assume-yes", + ], + ) + + # Assert it exists and has zero balance. + balance = int( + run_helper.api_client.account_balance(OTHER_ACCOUNT_ONE.account_address) + ) + if balance != 0: + raise TestError( + f"Account {OTHER_ACCOUNT_ONE.account_address} has balance {balance}, expected 0" + ) diff --git a/movement-sdk/clis/aptos/e2e/cases/init.py b/movement-sdk/clis/aptos/e2e/cases/init.py new file mode 100644 index 000000000..0ab32c8ba --- /dev/null +++ b/movement-sdk/clis/aptos/e2e/cases/init.py @@ -0,0 +1,43 @@ +# Copyright © Aptos Foundation +# SPDX-License-Identifier: Apache-2.0 + +import os + +from common import TestError +from test_helpers import RunHelper +from test_results import test_case + + +@test_case +def test_init(run_helper: RunHelper, test_name=None): + # Inititalize a profile for the CLI to use. Note that we do not set the + # --skip-faucet flag. This means that in addition to creating a profile locally, + # it will use the faucet to create the account on chain. This will fund the + # account with the default amount of 100000000 OCTA. + run_helper.run_command( + test_name, + ["movement", "init", "--assume-yes", "--network", "local"], + input="\n", + ) + + # Assert that the CLI config is there. + config_path = os.path.join( + run_helper.host_working_directory, ".movement", "config.yaml" + ) + if not os.path.exists(config_path): + raise TestError( + f"{config_path} not found (in host working dir) after running aptos init" + ) + + # Assert that it contains info for the account that was created. + account_info = run_helper.get_account_info() + if not account_info: + raise TestError("Failed to read account info from newly created config file") + + # Confirm with the local testnet that it was created. + try: + run_helper.api_client.account(account_info.account_address) + except Exception as e: + raise TestError( + f"Failed to query local testnet for account {account_info.account_address}" + ) from e diff --git a/movement-sdk/clis/aptos/e2e/common.py b/movement-sdk/clis/aptos/e2e/common.py new file mode 100644 index 000000000..0240163cf --- /dev/null +++ b/movement-sdk/clis/aptos/e2e/common.py @@ -0,0 +1,49 @@ +# Copyright © Aptos Foundation +# SPDX-License-Identifier: Apache-2.0 + +import os +from dataclasses import dataclass +from enum import Enum + +NODE_PORT = 8080 +FAUCET_PORT = 8081 + + +class Network(Enum): + DEVNET = "devnet" + TESTNET = "testnet" + MAINNET = "mainnet" + + def __str__(self): + return self.value + + +# Information for some accounts we use for testing. +@dataclass +class AccountInfo: + private_key: str + public_key: str + account_address: str + + +# This is an account that use for testing, for example to create it with the init +# account, send funds to it, etc. This is not the account created by the `aptos init` +# test. To get details about that account use get_account_info on the RunHelper. +OTHER_ACCOUNT_ONE = AccountInfo( + private_key="0x37368b46ce665362562c6d1d4ec01a08c8644c488690df5a17e13ba163e20221", + public_key="0x25caf00522e4d4664ec0a27166a69e8a32b5078959d0fc398da70d40d2893e8f", + account_address="0x585fc9f0f0c54183b039ffc770ca282ebd87307916c215a3e692f2f8e4305e82", +) + + +def build_image_name(image_repo_with_project: str, tag: str): + return f"{image_repo_with_project}/tools:{tag}" + + +# Exception to use when a test fails, for the CLI did something unexpected, an +# expected output was missing, etc. This is just a convenience, the framework +# will still work if a different error is raised. +# +# For errors within the framework itself, use RuntimeError. +class TestError(Exception): + pass diff --git a/movement-sdk/clis/aptos/e2e/local_testnet.py b/movement-sdk/clis/aptos/e2e/local_testnet.py new file mode 100644 index 000000000..49de4188d --- /dev/null +++ b/movement-sdk/clis/aptos/e2e/local_testnet.py @@ -0,0 +1,100 @@ +# Copyright © Aptos Foundation +# SPDX-License-Identifier: Apache-2.0 + +# This file contains functions for running the local testnet. + +import logging +import subprocess +import time + +import requests +from common import FAUCET_PORT, NODE_PORT, Network, build_image_name + +LOG = logging.getLogger(__name__) + +# Run a local testnet in a docker container. We choose to detach here and we'll +# stop running it later using the container name. +def run_node(network: Network, image_repo_with_project: str): + image_name = build_image_name(image_repo_with_project, network) + container_name = f"aptos-tools-{network}" + LOG.info(f"Trying to run movement CLI local testnet from image: {image_name}") + + # Confirm that the Docker daemon is running. + try: + subprocess.run( + ["docker", "container", "ls"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + check=True, + ) + except: + LOG.error("Failed to connect to Docker. Is it installed and running?") + raise + + # First delete the existing container if there is one with the same name. + subprocess.run( + ["docker", "rm", "-f", container_name], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + + # Run the container. + subprocess.check_output( + [ + "docker", + "run", + "--pull", + "always", + "--detach", + "--name", + container_name, + "-p", + f"{NODE_PORT}:{NODE_PORT}", + "-p", + f"{FAUCET_PORT}:{FAUCET_PORT}", + image_name, + "movement", + "node", + "run-local-testnet", + "--with-faucet", + ], + ) + LOG.info(f"Running movement CLI local testnet from image: {image_name}") + return container_name + + +# Stop running the detached node. +def stop_node(container_name: str): + LOG.info(f"Stopping container: {container_name}") + subprocess.check_output(["docker", "stop", container_name]) + LOG.info(f"Stopped container: {container_name}") + + +# Query the node and faucet APIs until they start up or we timeout. +def wait_for_startup(container_name: str, timeout: int): + LOG.info(f"Waiting for node and faucet APIs for {container_name} to come up") + count = 0 + api_response = None + faucet_response = None + while True: + try: + api_response = requests.get(f"http://127.0.0.1:{NODE_PORT}/v1") + # Try to query the legacy faucet health endpoint first. TODO: Remove this + # once all local testnet images we use have the new faucet in them. + faucet_response = requests.get(f"http://127.0.0.1:{FAUCET_PORT}/health") + if faucet_response.status_code == 404: + # If that fails, try the new faucet health endpoint. + faucet_response = requests.get(f"http://127.0.0.1:{FAUCET_PORT}/") + if api_response.status_code != 200 or faucet_response.status_code != 200: + raise RuntimeError( + f"API or faucet not ready. API response: {api_response}. " + f"Faucet response: {faucet_response}" + ) + break + except Exception: + if count >= timeout: + LOG.error(f"Timeout while waiting for node / faucet to come up") + raise + count += 1 + time.sleep(1) + LOG.info(f"Node and faucet APIs for {container_name} came up") diff --git a/movement-sdk/clis/aptos/e2e/main.py b/movement-sdk/clis/aptos/e2e/main.py new file mode 100644 index 000000000..2d8f97d6a --- /dev/null +++ b/movement-sdk/clis/aptos/e2e/main.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 + +# Copyright © Aptos Foundation +# SPDX-License-Identifier: Apache-2.0 + +""" +This script is how we orchestrate running a local testnet and then running CLI tests against it. There are two different CLIs used for this: + +1. Base: For running the local testnet. This is what the --base-network flag and all other flags starting with --base are for. +2. Test: The CLI that we're testing. This is what the --test-cli-tag / --test-cli-path and all other flags starting with --test are for. + +Example (testing CLI in image): + python3 main.py --base-network testnet --test-cli-tag mainnet_0431e2251d0b42920d89a52c63439f7b9eda6ac3 + +Example (testing locally built CLI binary): + python3 main.py --base-network devnet --test-cli-path ~/aptos-core/target/release/aptos + +This means, run the CLI test suite using a CLI built from mainnet_0431e2251d0b42920d89a52c63439f7b9eda6ac3 against a local testnet built from the testnet branch of aptos-core. + +Example (using a different image repo): + See ~/.github/workflows/cli-e2e-tests.yaml + +When the test suite is complete, it will tell you which tests passed and which failed. To further debug a failed test, you can check the output in --working-directory, there will be files for each test containing the command run, stdout, stderr, and any exception. +""" + +import argparse +import logging +import pathlib +import shutil +import sys + +from cases.account import test_account_create, test_account_fund_with_faucet +from cases.init import test_init +from common import Network +from local_testnet import run_node, stop_node, wait_for_startup +from test_helpers import RunHelper +from test_results import test_results + +logging.basicConfig( + stream=sys.stderr, + format="%(asctime)s - %(levelname)s - %(message)s", + level=logging.INFO, +) + +LOG = logging.getLogger(__name__) + + +def parse_args(): + # You'll notice there are two argument "prefixes", base and test. These refer to + # cases 1 and 2 in the top-level comment. + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=__doc__, + ) + parser.add_argument("-d", "--debug", action="store_true") + parser.add_argument( + "--image-repo-with-project", + default="aptoslabs", + help=( + "What docker image repo (+ project) to use for the local testnet. " + "By default we use Docker Hub: %(default)s (so, just aptoslabs for the " + "project since Docker Hub is the implied default repo). If you want to " + "specify a different repo, it might look like this: " + "docker.pkg.github.com/aptoslabs/aptos-core" + ), + ) + parser.add_argument( + "--base-network", + required=True, + type=Network, + choices=list(Network), + help="What branch the Movement CLI used for the local testnet should be built from", + ) + parser.add_argument( + "--base-startup-timeout", + type=int, + default=30, + help="Timeout in seconds for waiting for node and faucet to start up", + ) + test_cli_args = parser.add_mutually_exclusive_group(required=True) + test_cli_args.add_argument( + "--test-cli-tag", + help="The image tag for the CLI we want to test, e.g. mainnet_0431e2251d0b42920d89a52c63439f7b9eda6ac3", + ) + test_cli_args.add_argument( + "--test-cli-path", + help="Path to CLI binary we want to test, e.g. /home/dport/aptos-core/target/release/aptos", + ) + parser.add_argument( + "--working-directory", + default="/tmp/aptos-cli-tests", + help="Where we'll run CLI commands from (in the host system). Default: %(default)s", + ) + args = parser.parse_args() + return args + + +def run_tests(run_helper): + # Run init tests. We run these first to set up the CLI. + test_init(run_helper) + + # Run account tests. + test_account_fund_with_faucet(run_helper) + test_account_create(run_helper) + + +def main(): + args = parse_args() + + if args.debug: + logging.getLogger().setLevel(logging.DEBUG) + LOG.debug("Debug logging enabled") + else: + logging.getLogger().setLevel(logging.INFO) + + # Run a node + faucet and wait for them to start up. + container_name = run_node(args.base_network, args.image_repo_with_project) + wait_for_startup(container_name, args.base_startup_timeout) + + # Create the dir the test CLI will run from. + shutil.rmtree(args.working_directory, ignore_errors=True) + pathlib.Path(args.working_directory).mkdir(parents=True, exist_ok=True) + + # Build the RunHelper object. + run_helper = RunHelper( + host_working_directory=args.working_directory, + image_repo_with_project=args.image_repo_with_project, + image_tag=args.test_cli_tag, + cli_path=args.test_cli_path, + ) + + # Prepare the run helper. This ensures in advance that everything needed is there. + run_helper.prepare() + + # Run tests. + run_tests(run_helper) + + # Stop the node + faucet. + stop_node(container_name) + + # Print out the results. + if test_results.passed: + LOG.info("These tests passed:") + for test_name in test_results.passed: + LOG.info(test_name) + + if test_results.failed: + LOG.error("These tests failed:") + for test_name, exception in test_results.failed: + LOG.error(f"{test_name}: {exception}") + return False + + LOG.info("All tests passed!") + return True + + +if __name__ == "__main__": + if main(): + sys.exit(0) + else: + sys.exit(1) diff --git a/movement-sdk/clis/aptos/e2e/poetry.lock b/movement-sdk/clis/aptos/e2e/poetry.lock new file mode 100644 index 000000000..2708f4a9a --- /dev/null +++ b/movement-sdk/clis/aptos/e2e/poetry.lock @@ -0,0 +1,665 @@ +[[package]] +name = "anyio" +version = "3.6.2" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +category = "main" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] +trio = ["trio (>=0.16,<0.22)"] + +[[package]] +name = "aptos-sdk" +version = "0.5.1" +description = "" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +httpx = ">=0.23.0,<0.24.0" +mypy = ">=0.982,<0.983" +PyNaCl = ">=1.5.0,<2.0.0" + +[[package]] +name = "black" +version = "22.12.0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2022.12.7" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.1.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.7.0" + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} + +[[package]] +name = "httpcore" +version = "0.16.3" +description = "A minimal low-level HTTP client." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +anyio = ">=3.0,<5.0" +certifi = "*" +h11 = ">=0.13,<0.15" +sniffio = ">=1.0.0,<2.0.0" + +[package.extras] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + +[[package]] +name = "httpx" +version = "0.23.3" +description = "The next generation HTTP client." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +certifi = "*" +httpcore = ">=0.15.0,<0.17.0" +rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "importlib-metadata" +version = "6.0.0" +description = "Read metadata from Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + +[[package]] +name = "isort" +version = "5.11.5" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=3.7.0" + +[package.extras] +colors = ["colorama (>=0.4.3,<0.5.0)"] +pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] +plugins = ["setuptools"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] + +[[package]] +name = "mypy" +version = "0.982" +description = "Optional static typing for Python" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +mypy-extensions = ">=0.4.3" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "pathspec" +version = "0.11.0" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "platformdirs" +version = "3.1.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""} + +[package.extras] +docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pynacl" +version = "1.5.0" +description = "Python binding to the Networking and Cryptography (NaCl) library" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.4.1" + +[package.extras] +docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] +tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] + +[[package]] +name = "requests" +version = "2.28.2" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=3.7, <4" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rfc3986" +version = "1.5.0" +description = "Validating URI References per RFC 3986" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} + +[package.extras] +idna2008 = ["idna"] + +[[package]] +name = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "typed-ast" +version = "1.5.4" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "typing-extensions" +version = "4.5.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "urllib3" +version = "1.26.15" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "zipp" +version = "3.15.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-o", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "1.1" +python-versions = ">=3.7 <4" +content-hash = "e9e3c9c792c90300ff2f22bcfadc9ad737060eb3142c17a21e687073fa54e877" + +[metadata.files] +anyio = [ + {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, + {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, +] +aptos-sdk = [ + {file = "aptos_sdk-0.5.1.tar.gz", hash = "sha256:3711ad2bf1120fff463cd5f494162c4658f03dd6bfbf1f523ee9aea01a4cb0f0"}, +] +black = [ + {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, + {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, + {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, + {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, + {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, + {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, + {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, + {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, + {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, + {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, + {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, + {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, +] +certifi = [ + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, +] +cffi = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] +charset-normalizer = [ + {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, + {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, +] +click = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] +colorama = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] +h11 = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] +httpcore = [ + {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, + {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, +] +httpx = [ + {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"}, + {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"}, +] +idna = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] +importlib-metadata = [ + {file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"}, + {file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"}, +] +isort = [ + {file = "isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746"}, + {file = "isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db"}, +] +mypy = [ + {file = "mypy-0.982-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5085e6f442003fa915aeb0a46d4da58128da69325d8213b4b35cc7054090aed5"}, + {file = "mypy-0.982-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:41fd1cf9bc0e1c19b9af13a6580ccb66c381a5ee2cf63ee5ebab747a4badeba3"}, + {file = "mypy-0.982-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f793e3dd95e166b66d50e7b63e69e58e88643d80a3dcc3bcd81368e0478b089c"}, + {file = "mypy-0.982-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86ebe67adf4d021b28c3f547da6aa2cce660b57f0432617af2cca932d4d378a6"}, + {file = "mypy-0.982-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:175f292f649a3af7082fe36620369ffc4661a71005aa9f8297ea473df5772046"}, + {file = "mypy-0.982-cp310-cp310-win_amd64.whl", hash = "sha256:8ee8c2472e96beb1045e9081de8e92f295b89ac10c4109afdf3a23ad6e644f3e"}, + {file = "mypy-0.982-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58f27ebafe726a8e5ccb58d896451dd9a662a511a3188ff6a8a6a919142ecc20"}, + {file = "mypy-0.982-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6af646bd46f10d53834a8e8983e130e47d8ab2d4b7a97363e35b24e1d588947"}, + {file = "mypy-0.982-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7aeaa763c7ab86d5b66ff27f68493d672e44c8099af636d433a7f3fa5596d40"}, + {file = "mypy-0.982-cp37-cp37m-win_amd64.whl", hash = "sha256:724d36be56444f569c20a629d1d4ee0cb0ad666078d59bb84f8f887952511ca1"}, + {file = "mypy-0.982-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14d53cdd4cf93765aa747a7399f0961a365bcddf7855d9cef6306fa41de01c24"}, + {file = "mypy-0.982-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:26ae64555d480ad4b32a267d10cab7aec92ff44de35a7cd95b2b7cb8e64ebe3e"}, + {file = "mypy-0.982-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6389af3e204975d6658de4fb8ac16f58c14e1bacc6142fee86d1b5b26aa52bda"}, + {file = "mypy-0.982-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b35ce03a289480d6544aac85fa3674f493f323d80ea7226410ed065cd46f206"}, + {file = "mypy-0.982-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c6e564f035d25c99fd2b863e13049744d96bd1947e3d3d2f16f5828864506763"}, + {file = "mypy-0.982-cp38-cp38-win_amd64.whl", hash = "sha256:cebca7fd333f90b61b3ef7f217ff75ce2e287482206ef4a8b18f32b49927b1a2"}, + {file = "mypy-0.982-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a705a93670c8b74769496280d2fe6cd59961506c64f329bb179970ff1d24f9f8"}, + {file = "mypy-0.982-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75838c649290d83a2b83a88288c1eb60fe7a05b36d46cbea9d22efc790002146"}, + {file = "mypy-0.982-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:91781eff1f3f2607519c8b0e8518aad8498af1419e8442d5d0afb108059881fc"}, + {file = "mypy-0.982-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa97b9ddd1dd9901a22a879491dbb951b5dec75c3b90032e2baa7336777363b"}, + {file = "mypy-0.982-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a692a8e7d07abe5f4b2dd32d731812a0175626a90a223d4b58f10f458747dd8a"}, + {file = "mypy-0.982-cp39-cp39-win_amd64.whl", hash = "sha256:eb7a068e503be3543c4bd329c994103874fa543c1727ba5288393c21d912d795"}, + {file = "mypy-0.982-py3-none-any.whl", hash = "sha256:1021c241e8b6e1ca5a47e4d52601274ac078a89845cfde66c6d5f769819ffa1d"}, + {file = "mypy-0.982.tar.gz", hash = "sha256:85f7a343542dc8b1ed0a888cdd34dca56462654ef23aa673907305b260b3d746"}, +] +mypy-extensions = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] +pathspec = [ + {file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"}, + {file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"}, +] +platformdirs = [ + {file = "platformdirs-3.1.0-py3-none-any.whl", hash = "sha256:13b08a53ed71021350c9e300d4ea8668438fb0046ab3937ac9a29913a1a1350a"}, + {file = "platformdirs-3.1.0.tar.gz", hash = "sha256:accc3665857288317f32c7bebb5a8e482ba717b474f3fc1d18ca7f9214be0cef"}, +] +pycparser = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] +pynacl = [ + {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, + {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, +] +requests = [ + {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, + {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, +] +rfc3986 = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, +] +sniffio = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] +tomli = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] +typed-ast = [ + {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, + {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, + {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, + {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, + {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, + {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, + {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, + {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, + {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, + {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, +] +typing-extensions = [ + {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, + {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, +] +urllib3 = [ + {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, + {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, +] +zipp = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] diff --git a/movement-sdk/clis/aptos/e2e/pyproject.toml b/movement-sdk/clis/aptos/e2e/pyproject.toml new file mode 100644 index 000000000..b706f7df3 --- /dev/null +++ b/movement-sdk/clis/aptos/e2e/pyproject.toml @@ -0,0 +1,19 @@ +[tool.poetry] +name = "movement-cli-e2e-tests" +version = "0.1.0" +description = "Movement CLI E2E tests" +authors = ["Movment Labs "] +license = "Apache-2.0" + +[tool.poetry.dependencies] +python = ">=3.7 <4" +aptos-sdk = "^0.5.1" +requests = "^2.28.2" + +[tool.poetry.dev-dependencies] +black = "^22.6.0" +isort = "^5.10.1" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/movement-sdk/clis/aptos/e2e/test_helpers.py b/movement-sdk/clis/aptos/e2e/test_helpers.py new file mode 100644 index 000000000..e3af62d43 --- /dev/null +++ b/movement-sdk/clis/aptos/e2e/test_helpers.py @@ -0,0 +1,174 @@ +# Copyright © Aptos Foundation +# SPDX-License-Identifier: Apache-2.0 + +import logging +import os +import pathlib +import subprocess +import traceback +from dataclasses import dataclass + +from aptos_sdk.client import RestClient +from common import AccountInfo, build_image_name + +LOG = logging.getLogger(__name__) + +WORKING_DIR_IN_CONTAINER = "/tmp" + +# We pass this class into all test functions to help with calling the CLI, +# collecting output, and accessing common info. +@dataclass +class RunHelper: + host_working_directory: str + image_repo_with_project: str + image_tag: str + cli_path: str + test_count: int + + # This can be used by the tests to query the local testnet. + api_client: RestClient + + def __init__( + self, host_working_directory, image_repo_with_project, image_tag, cli_path + ): + if image_tag and cli_path: + raise RuntimeError("Cannot specify both image_tag and cli_path") + if not (image_tag or cli_path): + raise RuntimeError("Must specify one of image_tag and cli_path") + self.host_working_directory = host_working_directory + self.image_repo_with_project = image_repo_with_project + self.image_tag = image_tag + self.cli_path = os.path.abspath(cli_path) if cli_path else cli_path + self.test_count = 0 + self.api_client = RestClient(f"http://127.0.0.1:8080/v1") + + def build_image_name(self): + return build_image_name(self.image_repo_with_project, self.image_tag) + + # This function lets you pass call the CLI like you would normally, but really it is + # calling the CLI in a docker container and mounting the host working directory such + # that the container will write it results out to that directory. That way the CLI + # state / configuration is preserved between test cases. + def run_command(self, test_name, command, *args, **kwargs): + file_name = f"{self.test_count:03}_{test_name}" + self.test_count += 1 + + # Build command. + if self.image_tag: + full_command = [ + "docker", + "run", + # For why we have to set --user, see here: + # https://github.com/community/community/discussions/44243 + "--user", + f"{os.getuid()}:{os.getgid()}", + "--rm", + "--network", + "host", + "-i", + "-v", + f"{self.host_working_directory}:{WORKING_DIR_IN_CONTAINER}", + "--workdir", + WORKING_DIR_IN_CONTAINER, + self.build_image_name(), + ] + command + else: + full_command = [self.cli_path] + command[1:] + LOG.debug(f"Running command: {full_command}") + + # Create the output directory if necessary. + out_path = os.path.join(self.host_working_directory, "out") + pathlib.Path(out_path).mkdir(exist_ok=True) + + # Write the command we're going to run to file. + with open(os.path.join(out_path, f"{file_name}.command"), "w") as f: + f.write(" ".join(command)) + + # Run command. + try: + # If we're using a local CLI, set the working directory for subprocess.run. + if self.cli_path: + kwargs["cwd"] = self.host_working_directory + result = subprocess.run( + full_command, + *args, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + **kwargs, + ) + LOG.debug(f"Subcommand succeeded: {test_name}") + + write_subprocess_out(out_path, file_name, result) + + return result + except subprocess.CalledProcessError as e: + LOG.warn(f"Subcommand failed: {test_name}") + + # Write the exception to file. + with open(os.path.join(out_path, f"{file_name}.exception"), "w") as f: + f.write( + "".join( + traceback.format_exception( + etype=type(e), value=e, tb=e.__traceback__ + ) + ) + ) + + # Fortunately the result and exception of subprocess.run both have the + # stdout and stderr attributes on them. + write_subprocess_out(out_path, file_name, e) + + raise + + # If image_Tag is set, pull the test CLI image. We don't technically have to do + # this separately but it makes the steps clearer. Otherwise, cli_path must be + # set, in which case we ensure the file is there. + def prepare(self): + if self.image_tag: + image_name = self.build_image_name() + LOG.info(f"Pre-pulling image for CLI we're testing: {image_name}") + command = ["docker", "pull", image_name] + LOG.debug(f"Running command: {command}") + output = subprocess.check_output(command) + LOG.debug(f"Output: {output}") + else: + if not os.path.isfile(self.cli_path): + raise RuntimeError(f"CLI not found at path: {self.cli_path}") + + # Get the account info of the account created by test_init. + def get_account_info(self): + path = os.path.join(self.host_working_directory, ".aptos", "config.yaml") + with open(path) as f: + content = f.read().splitlines() + # To avoid using external deps we parse the file manually. + private_key = None + public_key = None + account_address = None + for line in content: + if "private_key: " in line: + private_key = line.split("private_key: ")[1].replace('"', "") + if "public_key: " in line: + public_key = line.split("public_key: ")[1].replace('"', "") + if "account: " in line: + account_address = line.split("account: ")[1].replace('"', "") + if not private_key or not public_key or not account_address: + raise RuntimeError(f"Failed to parse {path} to get account info") + return AccountInfo( + private_key=private_key, + public_key=public_key, + account_address=account_address, + ) + + +# This function helps with writing the stdout / stderr of a subprocess to files. +def write_subprocess_out(out_path, file_name, command_output): + LOG.debug(f"Stdout: {command_output.stdout}") + LOG.debug(f"Stderr: {command_output.stderr}") + + # Write stdout and stderr to file. + with open(os.path.join(out_path, f"{file_name}.stdout"), "w") as f: + f.write(command_output.stdout) + with open(os.path.join(out_path, f"{file_name}.stderr"), "w") as f: + f.write(command_output.stderr) diff --git a/movement-sdk/clis/aptos/e2e/test_results.py b/movement-sdk/clis/aptos/e2e/test_results.py new file mode 100644 index 000000000..661fe43be --- /dev/null +++ b/movement-sdk/clis/aptos/e2e/test_results.py @@ -0,0 +1,48 @@ +# Copyright © Aptos Foundation +# SPDX-License-Identifier: Apache-2.0 + +import logging +import typing +from dataclasses import dataclass, field +from functools import wraps + +LOG = logging.getLogger(__name__) + + +# This class holds info about passed / failed tests. +@dataclass(init=True) +class TestResults: + passed: typing.List[str] = field(default_factory=list) + failed: typing.List[typing.Tuple[str, Exception]] = field(default_factory=list) + + +# This is a decorator that you put above every test case. It handles capturing test +# success / failure so it can be reported at the end of the test suite. +def build_test_case_decorator(test_results: TestResults): + def test_case_inner(f): + @wraps(f) + def wrapper(*args, **kwds): + LOG.info(f"Running test: {f.__name__}") + try: + result = f(*args, test_name=f.__name__, **kwds) + test_results.passed.append(f.__name__) + return result + except Exception as e: + test_results.failed.append((f.__name__, e)) + return None + + return wrapper + + return test_case_inner + + +# We now define one TestResults that we'll use for every test case. This is a bit of a +# hack but it is the only way to then be able to provide a decorator that works out of +# the box. The alternative was to use a context manager and wrap every function call in +# it, but not only is that more verbose, but you'd have to provide the name of each test +# case manually to the context manager, whereas with this approach the name can be +# inferred from the function being decorated directly. +test_results = TestResults() + +# Then we define an instance of the decorator that uses that TestResults instance. +test_case = build_test_case_decorator(test_results) diff --git a/movement-sdk/clis/aptos/homebrew/README.md b/movement-sdk/clis/aptos/homebrew/README.md new file mode 100644 index 000000000..e25d87d3b --- /dev/null +++ b/movement-sdk/clis/aptos/homebrew/README.md @@ -0,0 +1,210 @@ +# Homebrew Aptos + +Homebrew is a package manager that works for MacOS Silicon and Intel chips as well as Linux distributions like Debian and Ubuntu. + +The [Aptos command line interface (CLI)](https://aptos.dev/cli-tools/aptos-cli-tool/install-aptos-cli) may be installed via [Homebrew](https://brew.sh/) for simplicity. This is an in-depth overview of Homebrew and the Aptos formula. In this guide, we go over each section of the Homebrew formula and steps to implement changes in the future. + +## Quick guide + +- [Formula in Homebrew GitHub](https://github.com/Homebrew/homebrew-core/blob/master/Formula/aptos.rb) +- [Aptos 1.0.3 New Formula PR for GitHub](https://github.com/Homebrew/homebrew-core/pull/119832) +- [Aptos Formula Fix PR to use build_cli_release.sh](https://github.com/Homebrew/homebrew-core/pull/120051) + +## Getting started + +To begin, first ensure that homebrew is correctly installed on your computer. Visit [brew.sh](https://brew.sh/) to learn how you can set it up! + +To test that it works correctly, try + +```bash +brew help +``` + +Once homebrew is installed, run + +```bash +brew install aptos +``` + +to test that it installed correctly, try + +```bash +movement --help + +# This should return something like + +# movement 1.0.5 +# Movement Labs +# Command Line Interface (CLI) for developing and interacting with the Aptos blockchain +# ... +``` + +## Change guide + +Note: This guide is for developers who are trying to update the Aptos homebrew formula. + +Copy the `aptos.rb` file to your `homebrew` `formula` directory. For example, on macOS with an M1, this will likely be: + +```bash +/opt/homebrew/Library/Taps/homebrew/homebrew-core/Formula +``` + + +### Development + +After you've copied `aptos.rb` to your local `homebrew` `formula` directory, you can modify it and use the commands below for testing. + +```bash +# On Mac M1, homebrew formulas are located locally at +/opt/homebrew/Library/Taps/homebrew/homebrew-core/Formula + +# Before submitting changes run +brew audit --new-formula movement # For new formula +brew audit movement --strict --online +brew install movement +brew test movement + +# For debugging issues during the installation process you can do +brew install movement --interactive # Interactive, gives you access to the shell +brew install movement -d # Debug mode + +# Livecheck +brew livecheck --debug movement +``` + +### Committing changes + +Once you have audited and tested your brew formula using the commands above, make sure you: + +1. Commit your changes to `aptos-core` in `crates/aptos/homebrew`. +2. Fork the Homebrew Core repository per [How to Open a Homebrew Pull Request](https://docs.brew.sh/How-To-Open-a-Homebrew-Pull-Request#formulae-related-pull-request). +3. Create a PR on the [Homebrew Core](https://github.com/Homebrew/homebrew-core/pulls) repo with your changes. + +## Aptos.rb structure overview + +### Header + +```ruby +class Aptos < Formula + desc "Layer 1 blockchain built to support fair access to decentralized assets for all" + homepage "https://aptoslabs.com/" + url "https://github.com/aptos-labs/aptos-core/archive/refs/tags/aptos-cli-v1.0.3.tar.gz" + sha256 "670bb6cb841cb8a65294878af9a4f03d4cba2a598ab4550061fed3a4b1fe4e98" + license "Apache-2.0" + ... +``` + +### Bottles + +[Bottles](https://docs.brew.sh/Bottles#pour-bottle-pour_bottle) are precompiled binaries. This way people don't need to compile from source every time. + +> Bottles for homebrew/core formulae are created by [Brew Test Bot](https://docs.brew.sh/Brew-Test-Bot) when a pull request is submitted. If the formula builds successfully on each supported platform and a maintainer approves the change, [Brew Test Bot](https://docs.brew.sh/Brew-Test-Bot) updates its bottle do block and uploads each bottle to GitHub Packages. + +```ruby + ... + # IMPORTANT: These are automatically generated, you DO NOT need to add these manually, I'm adding them here as an example + bottle do + sha256 cellar: :any_skip_relocation, arm64_ventura: "40434b61e99cf9114a3715851d01c09edaa94b814f89864d57a18d00a8e0c4e9" + sha256 cellar: :any_skip_relocation, arm64_monterey: "edd6dcf9d627746a910d324422085eb4b06cdab654789a03b37133cd4868633c" + sha256 cellar: :any_skip_relocation, arm64_big_sur: "d9568107514168afc41e73bd3fd0fc45a6a9891a289857831f8ee027fb339676" + sha256 cellar: :any_skip_relocation, ventura: "d7289b5efca029aaa95328319ccf1d8a4813c7828f366314e569993eeeaf0003" + sha256 cellar: :any_skip_relocation, monterey: "ba58e1eb3398c725207ce9d6251d29b549cde32644c3d622cd286b86c7896576" + sha256 cellar: :any_skip_relocation, big_sur: "3e2431a6316b8f0ffa4db75758fcdd9dea162fdfb3dbff56f5e405bcbea4fedc" + sha256 cellar: :any_skip_relocation, x86_64_linux: "925113b4967ed9d3da78cd12745b1282198694a7f8c11d75b8c41451f8eff4b5" + end + ... +``` + +### Livecheck + +[Brew livecheck](https://docs.brew.sh/Brew-Livecheck) uses strategies to find the newest version of a formula or cask’s software by checking upstream. The strategy used below checks for all `aptos-cli-v` tags for `aptos-core`. The regex ensures that releases for other, non-CLI builds are not factored into livecheck. + +Livecheck is run on a schedule with BrewTestBot and will update the bottles automatically on a schedule to ensure they're up to date. For more info on how BrewTestBot and brew livecheck works, please see the [How does BrewTestBot work and when does it update formulae?](https://github.com/Homebrew/discussions/discussions/3083) discussion. + +```ruby +... + # This livecheck scans the releases folder and looks for all releases + # with matching regex of href="/tag/aptos-cli-v". This + # is done to automatically check for new release versions of the CLI. + livecheck do + url :stable + regex(/^aptos-cli[._-]v?(\d+(?:\.\d+)+)$/i) + end +... +``` + +To run livecheck for testing, we recommend including the `--debug` argument: + +```bash +brew livecheck --debug aptos +``` + +### Depends on and installation + +- `depends_on` is for specifying other [homebrew formulas as dependencies](https://docs.brew.sh/Formula-Cookbook#specifying-other-formulae-as-dependencies). +- Currently, we use v1.64 of Rust, as specified in the `Cargo.toml` file of the project. If we were to use the latest stable build of Rust +going forward, we would modify the formula slightly. See the comments below for more details. + + +```ruby + # Installs listed homebrew dependencies before Aptos installation + # Dependencies needed: https://aptos.dev/cli-tools/build-aptos-cli + # See scripts/dev_setup.sh in aptos-core for more info + depends_on "cmake" => :build + depends_on "rustup-init" => :build + uses_from_macos "llvm" => :build + + on_linux do + depends_on "pkg-config" => :build + depends_on "zip" => :build + depends_on "openssl@3" + depends_on "systemd" + end + + # Currently must compile with the same rustc version specified in the + # root Cargo.toml file of aptos-core (currently it is pegged to Rust + # v1.64). In the future if it becomes compatible with the latest Rust + # toolchain, we can remove the use of rustup-init, replacing it with a + # depends_on "rust" => :build + # above and build the binary without rustup as a dependency + # + # Uses build_cli_release.sh for creating the compiled binaries. + # This drastically reduces their size (ie. 2.2 GB on Linux for release + # build becomes 40 MB when run with opt-level = "z", strip, lto, etc). + # See cargo.toml [profile.cli] section for more details + def install + system "#{Formula["rustup-init"].bin}/rustup-init", + "-qy", "--no-modify-path", "--default-toolchain", "1.64" + ENV.prepend_path "PATH", HOMEBREW_CACHE/"cargo_cache/bin" + system "./scripts/cli/build_cli_release.sh", "homebrew" + bin.install "target/cli/aptos" + end +``` + +### Tests + +To conduct tests, run: + +```bash +brew test aptos +``` + +The current test generates a new key via the Movement CLI and ensures the shell output matches the filename(s) for that key. + +```ruby + ... + test do + assert_match(/output.pub/i, shell_output("#{bin}/aptos key generate --output-file output")) + end + ... +``` + +## Supporting resources + +- To view other Homebrew-related FAQs or ask questions yourself, visit the [discussions board](https://github.com/orgs/Homebrew/discussions). +- For similar Rust-related build examples, we recommend: + - [`rustfmt.rb`](https://github.com/Homebrew/homebrew-core/blob/master/Formula/rustfmt.rb) + - [`solana.rb`](https://github.com/Homebrew/homebrew-core/blob/master/Formula/solana.rb) +- Finally, note these key Homebew guides: + - [Homebrew Formula Cookbook](https://docs.brew.sh/Formula-Cookbook) + - [Creating and Running Your Own Homebrew Tap - Rust Runbook](https://publishing-project.rivendellweb.net/creating-and-running-your-own-homebrew-tap/) diff --git a/movement-sdk/clis/aptos/homebrew/aptos.rb b/movement-sdk/clis/aptos/homebrew/aptos.rb new file mode 100644 index 000000000..8d99b784d --- /dev/null +++ b/movement-sdk/clis/aptos/homebrew/aptos.rb @@ -0,0 +1,35 @@ +class Aptos < Formula + desc "Layer 1 blockchain built to support fair access to decentralized assets for all" + homepage "https://aptoslabs.com/" + url "https://github.com/aptos-labs/aptos-core/archive/refs/tags/aptos-cli-v1.0.3.tar.gz" + sha256 "670bb6cb841cb8a65294878af9a4f03d4cba2a598ab4550061fed3a4b1fe4e98" + license "Apache-2.0" + + livecheck do + url :stable + regex(/^aptos-cli[._-]v?(\d+(?:\.\d+)+)$/i) + end + + depends_on "cmake" => :build + depends_on "rustup-init" => :build + uses_from_macos "llvm" => :build + + on_linux do + depends_on "pkg-config" => :build + depends_on "zip" => :build + depends_on "openssl@3" + depends_on "systemd" + end + + def install + system "#{Formula["rustup-init"].bin}/rustup-init", + "-qy", "--no-modify-path", "--default-toolchain", "1.64" + ENV.prepend_path "PATH", HOMEBREW_CACHE/"cargo_cache/bin" + system "./scripts/cli/build_cli_release.sh", "homebrew" + bin.install "target/cli/aptos" + end + + test do + assert_match(/output.pub/i, shell_output("#{bin}/aptos key generate --output-file output")) + end +end \ No newline at end of file diff --git a/movement-sdk/clis/aptos/src/account/create.rs b/movement-sdk/clis/aptos/src/account/create.rs new file mode 100644 index 000000000..32070a403 --- /dev/null +++ b/movement-sdk/clis/aptos/src/account/create.rs @@ -0,0 +1,41 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::types::{CliCommand, CliTypedResult, TransactionOptions, TransactionSummary}; +use aptos_cached_packages::aptos_stdlib; +use aptos_types::account_address::AccountAddress; +use async_trait::async_trait; +use clap::Parser; + +// 1 APT +pub const DEFAULT_FUNDED_COINS: u64 = 100_000_000; + +/// Create a new account on-chain +/// +/// An account can be created by transferring coins, or by making an explicit +/// call to create an account. This will create an account with no coins, and +/// any coins will have to transferred afterwards. +#[derive(Debug, Parser)] +pub struct CreateAccount { + /// Address of the new account + #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] + pub(crate) account: AccountAddress, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for CreateAccount { + fn command_name(&self) -> &'static str { + "CreateAccount" + } + + async fn execute(self) -> CliTypedResult { + let address = self.account; + self.txn_options + .submit_transaction(aptos_stdlib::aptos_account_create_account(address)) + .await + .map(TransactionSummary::from) + } +} diff --git a/movement-sdk/clis/aptos/src/account/create_resource_account.rs b/movement-sdk/clis/aptos/src/account/create_resource_account.rs new file mode 100644 index 000000000..6e79071ab --- /dev/null +++ b/movement-sdk/clis/aptos/src/account/create_resource_account.rs @@ -0,0 +1,91 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + account::derive_resource_account::ResourceAccountSeed, + common::types::{CliCommand, CliTypedResult, TransactionOptions, TransactionSummary}, +}; +use aptos_cached_packages::aptos_stdlib::resource_account_create_resource_account; +use aptos_rest_client::{ + aptos_api_types::{WriteResource, WriteSetChange}, + Transaction, +}; +use aptos_types::{account_address::AccountAddress, transaction::authenticator::AuthenticationKey}; +use async_trait::async_trait; +use clap::Parser; +use serde::Serialize; +use std::str::FromStr; + +/// Create a resource account on-chain +/// +/// This will create a resource account which can be used as an autonomous account +/// not controlled directly by one account. +#[derive(Debug, Parser)] +pub struct CreateResourceAccount { + /// Optional Resource Account authentication key. + #[clap(long, parse(try_from_str = AuthenticationKey::from_str))] + pub(crate) authentication_key: Option, + + #[clap(flatten)] + pub(crate) seed_args: ResourceAccountSeed, + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +/// A shortened create resource account output +#[derive(Clone, Debug, Serialize)] +pub struct CreateResourceAccountSummary { + pub resource_account: Option, + #[serde(flatten)] + pub transaction_summary: TransactionSummary, +} + +impl From for CreateResourceAccountSummary { + fn from(transaction: Transaction) -> Self { + let transaction_summary = TransactionSummary::from(&transaction); + + let mut summary = CreateResourceAccountSummary { + transaction_summary, + resource_account: None, + }; + + if let Transaction::UserTransaction(txn) = transaction { + summary.resource_account = txn.info.changes.iter().find_map(|change| match change { + WriteSetChange::WriteResource(WriteResource { address, data, .. }) => { + if data.typ.name.as_str() == "Account" + && *address.inner().to_hex() != *txn.request.sender.inner().to_hex() + { + Some(*address.inner()) + } else { + None + } + }, + _ => None, + }); + } + + summary + } +} + +#[async_trait] +impl CliCommand for CreateResourceAccount { + fn command_name(&self) -> &'static str { + "CreateResourceAccount" + } + + async fn execute(self) -> CliTypedResult { + let authentication_key: Vec = if let Some(key) = self.authentication_key { + bcs::to_bytes(&key)? + } else { + vec![] + }; + self.txn_options + .submit_transaction(resource_account_create_resource_account( + self.seed_args.seed()?, + authentication_key, + )) + .await + .map(CreateResourceAccountSummary::from) + } +} diff --git a/movement-sdk/clis/aptos/src/account/derive_resource_account.rs b/movement-sdk/clis/aptos/src/account/derive_resource_account.rs new file mode 100644 index 000000000..2b14449df --- /dev/null +++ b/movement-sdk/clis/aptos/src/account/derive_resource_account.rs @@ -0,0 +1,114 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::types::{CliCommand, CliError, CliTypedResult}; +use aptos_sdk::rest_client::aptos_api_types::HexEncodedBytes; +use aptos_types::account_address::{create_resource_address, AccountAddress}; +use async_trait::async_trait; +use clap::Parser; +use std::{fmt::Formatter, str::FromStr}; + +/// Encoding for the Resource account seed +#[derive(Debug, Clone, Copy)] +pub enum SeedEncoding { + Bcs, + Hex, + Utf8, +} + +const BCS: &str = "bcs"; +const UTF_8: &str = "utf8"; +const HEX: &str = "hex"; + +impl Default for SeedEncoding { + fn default() -> Self { + SeedEncoding::Bcs + } +} + +impl std::fmt::Display for SeedEncoding { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + SeedEncoding::Bcs => BCS, + SeedEncoding::Hex => HEX, + SeedEncoding::Utf8 => UTF_8, + }) + } +} + +impl FromStr for SeedEncoding { + type Err = CliError; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + BCS => Ok(Self::Bcs), + HEX => Ok(Self::Hex), + UTF_8 | "utf-8" | "utf_8" => Ok(Self::Utf8), + _ => Err(CliError::UnableToParse( + "seed-encoding", + "For --seed-encoding please provide one of ['bcs','hex', 'utf8']".to_string(), + )), + } + } +} + +/// A generic interface for allowing for different types of seed phrase inputs +/// +/// The easiest to use is `string_seed` as it will match directly with the b"string" notation in Move. +#[derive(Debug, Parser)] +pub struct ResourceAccountSeed { + /// Resource account seed + /// + /// Seed used in generation of the AccountId of the resource account + /// The seed will be converted to bytes using the encoding from `--seed-encoding`, defaults to `BCS` + #[clap(long)] + pub(crate) seed: String, + + /// Resource account seed encoding + /// + /// The encoding can be one of `Bcs`, `Utf8`, and `Hex`. + /// + /// - Bcs is the legacy functionality of the CLI, it will BCS encode the string, but can be confusing for users e.g. `"ab" -> vector[0x2, 0x61, 0x62]` + /// - Utf8 will encode the string as raw UTF-8 bytes, similar to in Move `b"string"` e.g. `"ab" -> vector[0x61, 0x62]` + /// - Hex will encode the string as raw hex encoded bytes e.g. `"0x6162" -> vector[0x61, 0x62]` + #[clap(long, default_value_t = SeedEncoding::Bcs)] + pub(crate) seed_encoding: SeedEncoding, +} + +impl ResourceAccountSeed { + pub fn seed(self) -> CliTypedResult> { + match self.seed_encoding { + SeedEncoding::Bcs => Ok(bcs::to_bytes(self.seed.as_str())?), + SeedEncoding::Utf8 => Ok(self.seed.as_bytes().to_vec()), + SeedEncoding::Hex => HexEncodedBytes::from_str(self.seed.as_str()) + .map(|inner| inner.0) + .map_err(|err| CliError::UnableToParse("seed", err.to_string())), + } + } +} + +/// Derive the address for a resource account +/// +/// This will not create a resource account, but instead give the deterministic address given +/// a source address and seed. +#[derive(Debug, Parser)] +pub struct DeriveResourceAccount { + /// Address of the creator's account + #[clap(long, alias = "account", parse(try_from_str=crate::common::types::load_account_arg))] + pub(crate) address: AccountAddress, + + #[clap(flatten)] + pub(crate) seed_args: ResourceAccountSeed, +} + +#[async_trait] +impl CliCommand for DeriveResourceAccount { + fn command_name(&self) -> &'static str { + "DeriveResourceAccountAddress" + } + + async fn execute(self) -> CliTypedResult { + let seed = self.seed_args.seed()?; + Ok(create_resource_address(self.address, &seed)) + } +} diff --git a/movement-sdk/clis/aptos/src/account/fund.rs b/movement-sdk/clis/aptos/src/account/fund.rs new file mode 100644 index 000000000..c3846aad6 --- /dev/null +++ b/movement-sdk/clis/aptos/src/account/fund.rs @@ -0,0 +1,69 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + account::create::DEFAULT_FUNDED_COINS, + common::{ + types::{CliCommand, CliTypedResult, FaucetOptions, ProfileOptions, RestOptions}, + utils::{fund_account, wait_for_transactions, fund_pub_key}, + }, +}; +use aptos_types::account_address::AccountAddress; +use async_trait::async_trait; +use clap::Parser; + +/// Fund an account with tokens from a faucet +/// +/// This will create an account if it doesn't exist with the faucet. This is mostly useful +/// for local development and devnet. +#[derive(Debug, Parser)] +pub struct FundWithFaucet { + /// Address to fund + /// + /// If the account wasn't previously created, it will be created when being funded + #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] + pub(crate) account: AccountAddress, + + /// Number of Octas to fund the account from the faucet + /// + /// The amount added to the account may be limited by the faucet, and may be less + /// than the amount requested. + #[clap(long, default_value_t = DEFAULT_FUNDED_COINS)] + pub(crate) amount: u64, + + #[clap(flatten)] + pub(crate) faucet_options: FaucetOptions, + #[clap(flatten)] + pub(crate) rest_options: RestOptions, + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, +} + +#[async_trait] +impl CliCommand for FundWithFaucet { + fn command_name(&self) -> &'static str { + "FundWithFaucet" + } + + async fn execute(self) -> CliTypedResult { + // todo: the below is a hotfix. Determine why the auth_key parameter in the rpc is not working + /* + let hashes = fund_account( + self.faucet_options.faucet_url(&self.profile_options)?, + self.amount, + self.account, + ) + .await?; + */ + let hashes = fund_pub_key( + self.faucet_options.faucet_url(&self.profile_options)?, + (self.profile_options.public_key()?).to_string() + ).await?; + let client = self.rest_options.client(&self.profile_options)?; + wait_for_transactions(&client, hashes).await?; + return Ok(format!( + "Added 10 MOV to account {}", + self.account + )); + } +} diff --git a/movement-sdk/clis/aptos/src/account/key_rotation.rs b/movement-sdk/clis/aptos/src/account/key_rotation.rs new file mode 100644 index 000000000..da008fb82 --- /dev/null +++ b/movement-sdk/clis/aptos/src/account/key_rotation.rs @@ -0,0 +1,338 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::{ + types::{ + account_address_from_public_key, CliCommand, CliConfig, CliError, CliTypedResult, + ConfigSearchMode, EncodingOptions, EncodingType, ExtractPublicKey, ParsePrivateKey, + ProfileConfig, ProfileOptions, PublicKeyInputOptions, RestOptions, RotationProofChallenge, + TransactionOptions, TransactionSummary, + }, + utils::{prompt_yes, prompt_yes_with_override, read_line}, +}; +use aptos_cached_packages::aptos_stdlib; +use aptos_crypto::{ + ed25519::{Ed25519PrivateKey, Ed25519PublicKey}, + PrivateKey, SigningKey, +}; +use aptos_rest_client::{ + aptos_api_types::{AptosError, AptosErrorCode}, + error::{AptosErrorResponse, RestError}, + Client, +}; +use aptos_types::{account_address::AccountAddress, account_config::CORE_CODE_ADDRESS}; +use async_trait::async_trait; +use clap::Parser; +use serde::{Deserialize, Serialize}; +use std::{collections::BTreeMap, path::PathBuf}; + +/// Rotate an account's authentication key +/// +/// Rotating the account's authentication key allows you to use a new +/// private key. You must provide a new private key. Once it is +/// rotated you will need to use the original account address, with the +/// new private key. There is an interactive prompt to help you add it +/// to a new profile. +#[derive(Debug, Parser)] +pub struct RotateKey { + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, + + /// File name that contains the new private key encoded in the type from `--encoding` + #[clap(long, group = "new_private_key", parse(from_os_str))] + pub(crate) new_private_key_file: Option, + + /// New private key encoded in the type from `--encoding` + #[clap(long, group = "new_private_key")] + pub(crate) new_private_key: Option, + + /// Name of the profile to save the new private key + /// + /// If not provided, it will interactively have you save a profile, + /// unless `--skip_saving_profile` is provided + #[clap(long)] + pub(crate) save_to_profile: Option, + + /// Skip saving profile + /// + /// This skips the interactive profile saving after rotating the authentication key + #[clap(long)] + pub(crate) skip_saving_profile: bool, +} + +impl ParsePrivateKey for RotateKey {} + +impl RotateKey { + /// Extract private key from CLI args + pub fn extract_private_key( + &self, + encoding: EncodingType, + ) -> CliTypedResult> { + self.parse_private_key( + encoding, + self.new_private_key_file.clone(), + self.new_private_key.clone(), + ) + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct RotateSummary { + message: Option, + transaction: TransactionSummary, +} + +#[async_trait] +impl CliCommand for RotateKey { + fn command_name(&self) -> &'static str { + "RotateKey" + } + + async fn execute(self) -> CliTypedResult { + let new_private_key = self + .extract_private_key(self.txn_options.encoding_options.encoding)? + .ok_or_else(|| { + CliError::CommandArgumentError( + "One of ['--new-private-key', '--new-private-key-file'] must be used" + .to_string(), + ) + })?; + + let (current_private_key, sender_address) = self.txn_options.get_key_and_address()?; + + // Get sequence number for account + let sequence_number = self.txn_options.sequence_number(sender_address).await?; + let auth_key = self.txn_options.auth_key(sender_address).await?; + + let rotation_proof = RotationProofChallenge { + account_address: CORE_CODE_ADDRESS, + module_name: "account".to_string(), + struct_name: "RotationProofChallenge".to_string(), + sequence_number, + originator: sender_address, + current_auth_key: AccountAddress::from_bytes(auth_key) + .map_err(|err| CliError::UnableToParse("auth_key", err.to_string()))?, + new_public_key: new_private_key.public_key().to_bytes().to_vec(), + }; + + let rotation_msg = + bcs::to_bytes(&rotation_proof).map_err(|err| CliError::BCS("rotation_proof", err))?; + + // Signs the struct using both the current private key and the next private key + let rotation_proof_signed_by_current_private_key = + current_private_key.sign_arbitrary_message(&rotation_msg.clone()); + let rotation_proof_signed_by_new_private_key = + new_private_key.sign_arbitrary_message(&rotation_msg); + + let txn_summary = self + .txn_options + .submit_transaction(aptos_stdlib::account_rotate_authentication_key( + 0, + // Existing public key + current_private_key.public_key().to_bytes().to_vec(), + 0, + // New public key + new_private_key.public_key().to_bytes().to_vec(), + rotation_proof_signed_by_current_private_key + .to_bytes() + .to_vec(), + rotation_proof_signed_by_new_private_key.to_bytes().to_vec(), + )) + .await + .map(TransactionSummary::from)?; + + let string = serde_json::to_string_pretty(&txn_summary) + .map_err(|err| CliError::UnableToParse("transaction summary", err.to_string()))?; + + eprintln!("{}", string); + + if let Some(txn_success) = txn_summary.success { + if !txn_success { + return Err(CliError::ApiError( + "Transaction was not executed successfully".to_string(), + )); + } + } else { + return Err(CliError::UnexpectedError( + "Malformed transaction response".to_string(), + )); + } + + let mut profile_name: String; + + if self.save_to_profile.is_none() { + if self.skip_saving_profile + || !prompt_yes("Do you want to create a profile for the new key?") + { + return Ok(RotateSummary { + transaction: txn_summary, + message: None, + }); + } + + eprintln!("Enter the name for the profile"); + profile_name = read_line("Profile name")?.trim().to_string(); + } else { + // We can safely unwrap here + profile_name = self.save_to_profile.unwrap(); + } + + // Check if profile name exists + let mut config = CliConfig::load(ConfigSearchMode::CurrentDirAndParents)?; + + if let Some(ref profiles) = config.profiles { + if profiles.contains_key(&profile_name) { + if let Err(cli_err) = prompt_yes_with_override( + format!( + "Profile {} exits. Do you want to provide a new profile name?", + profile_name + ) + .as_str(), + self.txn_options.prompt_options, + ) { + match cli_err { + CliError::AbortedError => { + return Ok(RotateSummary { + transaction: txn_summary, + message: None, + }); + } + _ => { + return Err(cli_err); + } + } + } + + eprintln!("Enter the name for the profile"); + profile_name = read_line("Profile name")?.trim().to_string(); + } + } + + if profile_name.is_empty() { + return Err(CliError::AbortedError); + } + + let mut profile_config = ProfileConfig { + private_key: Some(new_private_key.clone()), + public_key: Some(new_private_key.public_key()), + account: Some(sender_address), + ..self.txn_options.profile_options.profile()? + }; + + if let Some(url) = self.txn_options.rest_options.url { + profile_config.rest_url = Some(url.into()); + } + + if config.profiles.is_none() { + config.profiles = Some(BTreeMap::new()); + } + + config + .profiles + .as_mut() + .unwrap() + .insert(profile_name.clone(), profile_config); + config.save()?; + + eprintln!("Profile {} is saved.", profile_name); + + Ok(RotateSummary { + transaction: txn_summary, + message: Some(format!("Profile {} is saved.", profile_name)), + }) + } +} + +/// Lookup the account address through the on-chain lookup table +/// +/// If the account is rotated, it will provide the address accordingly. If the account was not +/// rotated, it will provide the derived address only if the account exists onchain. +#[derive(Debug, Parser)] +pub struct LookupAddress { + #[clap(flatten)] + pub(crate) encoding_options: EncodingOptions, + + #[clap(flatten)] + pub(crate) public_key_options: PublicKeyInputOptions, + + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, + + #[clap(flatten)] + pub(crate) rest_options: RestOptions, +} + +impl LookupAddress { + pub(crate) fn public_key(&self) -> CliTypedResult { + self.public_key_options + .extract_public_key(self.encoding_options.encoding, &self.profile_options) + } + + /// Builds a rest client + fn rest_client(&self) -> CliTypedResult { + self.rest_options.client(&self.profile_options) + } +} + +#[async_trait] +impl CliCommand for LookupAddress { + fn command_name(&self) -> &'static str { + "LookupAddress" + } + + async fn execute(self) -> CliTypedResult { + let rest_client = self.rest_client()?; + + // TODO: Support arbitrary auth key to support other types like multie25519 + let address = account_address_from_public_key(&self.public_key()?); + Ok(lookup_address(&rest_client, address, true).await?) + } +} + +pub async fn lookup_address( + rest_client: &Client, + address_key: AccountAddress, + must_exist: bool, +) -> Result { + let originating_resource: OriginatingResource = rest_client + .get_account_resource_bcs(CORE_CODE_ADDRESS, "0x1::account::OriginatingAddress") + .await? + .into_inner(); + + let table_handle = originating_resource.address_map.handle; + + // The derived address that can be used to look up the original address + match rest_client + .get_table_item_bcs( + table_handle, + "address", + "address", + address_key.to_hex_literal(), + ) + .await + { + Ok(inner) => Ok(inner.into_inner()), + Err(RestError::Api(..)) => { + // If the table item wasn't found, we may check if the account exists + if !must_exist { + Ok(address_key) + } else { + rest_client + .get_account_bcs(address_key) + .await + .map(|_| address_key) + } + } + Err(err) => Err(err), + } +} + +#[derive(Deserialize)] +pub struct OriginatingResource { + pub address_map: Table, +} + +#[derive(Deserialize)] +pub struct Table { + pub handle: AccountAddress, +} diff --git a/movement-sdk/clis/aptos/src/account/list.rs b/movement-sdk/clis/aptos/src/account/list.rs new file mode 100644 index 000000000..c406b2963 --- /dev/null +++ b/movement-sdk/clis/aptos/src/account/list.rs @@ -0,0 +1,124 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::types::{ + CliCommand, CliConfig, CliError, CliTypedResult, ConfigSearchMode, ProfileOptions, RestOptions, +}; +use aptos_types::account_address::AccountAddress; +use async_trait::async_trait; +use clap::{ArgEnum, Parser}; +use serde_json::json; +use std::{ + fmt::{Display, Formatter}, + str::FromStr, +}; + +#[derive(ArgEnum, Clone, Copy, Debug)] +pub enum ListQuery { + Balance, + Modules, + Resources, +} + +impl Display for ListQuery { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let str = match self { + ListQuery::Balance => "balance", + ListQuery::Modules => "modules", + ListQuery::Resources => "resources", + }; + write!(f, "{}", str) + } +} + +impl FromStr for ListQuery { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "balance" => Ok(ListQuery::Balance), + "modules" => Ok(ListQuery::Modules), + "resources" => Ok(ListQuery::Resources), + _ => Err("Invalid query. Valid values are balance, modules, resources"), + } + } +} + +/// List resources, modules, or balance owned by an address +/// +/// This allows you to list the current resources at the time of query. This can change due to +/// any transactions that have occurred after the request. +#[derive(Debug, Parser)] +pub struct ListAccount { + /// Address of the account you want to list resources/modules/balance for + #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] + pub(crate) account: Option, + + /// Type of items to list: [balance, resources, modules] + #[clap(long, default_value_t = ListQuery::Resources)] + pub(crate) query: ListQuery, + + #[clap(flatten)] + pub(crate) rest_options: RestOptions, + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, +} + +#[async_trait] +impl CliCommand> for ListAccount { + fn command_name(&self) -> &'static str { + "ListAccount" + } + + async fn execute(self) -> CliTypedResult> { + let account = if let Some(account) = self.account { + account + } else if let Some(Some(account)) = CliConfig::load_profile( + self.profile_options.profile_name(), + ConfigSearchMode::CurrentDirAndParents, + )? + .map(|p| p.account) + { + account + } else { + return Err(CliError::CommandArgumentError( + "Please provide an account using --account or run movement init".to_string(), + )); + }; + + let client = self.rest_options.client(&self.profile_options)?; + let response = match self.query { + ListQuery::Balance => vec![ + client + .get_account_resource( + account, + "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>", + ) + .await? + .into_inner() + .unwrap() + .data, + ], + ListQuery::Modules => client + .get_account_modules(account) + .await? + .into_inner() + .into_iter() + .map(|module| json!(module.try_parse_abi().unwrap())) + .collect::>(), + ListQuery::Resources => client + .get_account_resources(account) + .await? + .into_inner() + .into_iter() + .map(|resource| { + let mut map = serde_json::Map::new(); + map.insert(resource.resource_type.to_string(), resource.data); + serde_json::Value::Object(map) + }) + .collect::>(), + }; + + Ok(response) + } +} diff --git a/movement-sdk/clis/aptos/src/account/mod.rs b/movement-sdk/clis/aptos/src/account/mod.rs new file mode 100644 index 000000000..988f065a3 --- /dev/null +++ b/movement-sdk/clis/aptos/src/account/mod.rs @@ -0,0 +1,69 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::types::{CliCommand, CliResult}; +use clap::Subcommand; + +pub mod create; +pub mod create_resource_account; +pub mod derive_resource_account; +pub mod fund; +pub mod key_rotation; +pub mod list; +pub mod multisig_account; +pub mod transfer; + +/// Tool for interacting with accounts +/// +/// This tool is used to create accounts, get information about the +/// account's resources, and transfer resources between accounts. +#[derive(Debug, Subcommand)] +pub enum AccountTool { + Create(create::CreateAccount), + CreateResourceAccount(create_resource_account::CreateResourceAccount), + DeriveResourceAccountAddress(derive_resource_account::DeriveResourceAccount), + FundWithFaucet(fund::FundWithFaucet), + List(list::ListAccount), + LookupAddress(key_rotation::LookupAddress), + RotateKey(key_rotation::RotateKey), + Transfer(transfer::TransferCoins), +} + +impl AccountTool { + pub async fn execute(self) -> CliResult { + match self { + AccountTool::Create(tool) => tool.execute_serialized().await, + AccountTool::CreateResourceAccount(tool) => tool.execute_serialized().await, + AccountTool::DeriveResourceAccountAddress(tool) => tool.execute_serialized().await, + AccountTool::FundWithFaucet(tool) => tool.execute_serialized().await, + AccountTool::List(tool) => tool.execute_serialized().await, + AccountTool::LookupAddress(tool) => tool.execute_serialized().await, + AccountTool::RotateKey(tool) => tool.execute_serialized().await, + AccountTool::Transfer(tool) => tool.execute_serialized().await, + } + } +} + +/// Tool for interacting with multisig accounts +#[derive(Debug, Subcommand)] +pub enum MultisigAccountTool { + Approve(multisig_account::Approve), + Create(multisig_account::Create), + CreateTransaction(multisig_account::CreateTransaction), + Execute(multisig_account::Execute), + ExecuteReject(multisig_account::ExecuteReject), + Reject(multisig_account::Reject), +} + +impl MultisigAccountTool { + pub async fn execute(self) -> CliResult { + match self { + MultisigAccountTool::Approve(tool) => tool.execute_serialized().await, + MultisigAccountTool::Create(tool) => tool.execute_serialized().await, + MultisigAccountTool::CreateTransaction(tool) => tool.execute_serialized().await, + MultisigAccountTool::Execute(tool) => tool.execute_serialized().await, + MultisigAccountTool::ExecuteReject(tool) => tool.execute_serialized().await, + MultisigAccountTool::Reject(tool) => tool.execute_serialized().await, + } + } +} diff --git a/movement-sdk/clis/aptos/src/account/multisig_account.rs b/movement-sdk/clis/aptos/src/account/multisig_account.rs new file mode 100644 index 000000000..0d60380af --- /dev/null +++ b/movement-sdk/clis/aptos/src/account/multisig_account.rs @@ -0,0 +1,257 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::types::{ + CliCommand, CliTypedResult, EntryFunctionArguments, MultisigAccount, TransactionOptions, + TransactionSummary, +}; +use aptos_cached_packages::aptos_stdlib; +use aptos_rest_client::{ + aptos_api_types::{WriteResource, WriteSetChange}, + Transaction, +}; +use aptos_types::{ + account_address::AccountAddress, + transaction::{Multisig, MultisigTransactionPayload, TransactionPayload}, +}; +use async_trait::async_trait; +use bcs::to_bytes; +use clap::Parser; +use serde::Serialize; + +/// Create a new multisig account (v2) on-chain. +/// +/// This will create a new multisig account and make the sender one of the owners. +#[derive(Debug, Parser)] +pub struct Create { + /// Addresses of additional owners for the new multisig, beside the transaction sender. + #[clap(long, multiple_values = true, parse(try_from_str=crate::common::types::load_account_arg))] + pub(crate) additional_owners: Vec, + /// The number of signatures (approvals or rejections) required to execute or remove a proposed + /// transaction. + #[clap(long)] + pub(crate) num_signatures_required: u64, + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +/// A shortened create multisig account output +#[derive(Clone, Debug, Serialize)] +pub struct CreateSummary { + #[serde(flatten)] + pub multisig_account: Option, + #[serde(flatten)] + pub transaction_summary: TransactionSummary, +} + +impl From for CreateSummary { + fn from(transaction: Transaction) -> Self { + let transaction_summary = TransactionSummary::from(&transaction); + + let mut summary = CreateSummary { + transaction_summary, + multisig_account: None, + }; + + if let Transaction::UserTransaction(txn) = transaction { + summary.multisig_account = txn.info.changes.iter().find_map(|change| match change { + WriteSetChange::WriteResource(WriteResource { address, data, .. }) => { + if data.typ.name.as_str() == "Account" + && *address.inner().to_hex() != *txn.request.sender.inner().to_hex() + { + Some(MultisigAccount { + multisig_address: *address.inner(), + }) + } else { + None + } + }, + _ => None, + }); + } + + summary + } +} + +#[async_trait] +impl CliCommand for Create { + fn command_name(&self) -> &'static str { + "CreateMultisig" + } + + async fn execute(self) -> CliTypedResult { + self.txn_options + .submit_transaction(aptos_stdlib::multisig_account_create_with_owners( + self.additional_owners, + self.num_signatures_required, + // TODO: Support passing in custom metadata. + vec![], + vec![], + )) + .await + .map(CreateSummary::from) + } +} + +/// Propose a new multisig transaction. +/// +/// As one of the owners of the multisig, propose a new transaction. This also implicitly approves +/// the created transaction so it has one approval initially. In order for the transaction to be +/// executed, it needs as many approvals as the number of signatures required. +#[derive(Debug, Parser)] +pub struct CreateTransaction { + #[clap(flatten)] + pub(crate) multisig_account: MultisigAccount, + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, + #[clap(flatten)] + pub(crate) entry_function_args: EntryFunctionArguments, +} + +#[async_trait] +impl CliCommand for CreateTransaction { + fn command_name(&self) -> &'static str { + "CreateTransactionMultisig" + } + + async fn execute(self) -> CliTypedResult { + let payload = MultisigTransactionPayload::EntryFunction( + self.entry_function_args.create_entry_function_payload()?, + ); + self.txn_options + .submit_transaction(aptos_stdlib::multisig_account_create_transaction( + self.multisig_account.multisig_address, + to_bytes(&payload)?, + )) + .await + .map(|inner| inner.into()) + } +} + +/// Approve a multisig transaction. +/// +/// As one of the owners of the multisig, approve a transaction proposed for the multisig. +/// With enough approvals (as many as the number of signatures required), the transaction can be +/// executed (See Execute). +#[derive(Debug, Parser)] +pub struct Approve { + #[clap(flatten)] + pub(crate) multisig_account: MultisigAccount, + /// The sequence number of the multisig transaction to approve. The sequence number increments + /// for every new multisig transaction. + #[clap(long)] + pub(crate) sequence_number: u64, + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for Approve { + fn command_name(&self) -> &'static str { + "ApproveMultisig" + } + + async fn execute(self) -> CliTypedResult { + self.txn_options + .submit_transaction(aptos_stdlib::multisig_account_approve_transaction( + self.multisig_account.multisig_address, + self.sequence_number, + )) + .await + .map(|inner| inner.into()) + } +} + +/// Reject a multisig transaction. +/// +/// As one of the owners of the multisig, reject a transaction proposed for the multisig. +/// With enough rejections (as many as the number of signatures required), the transaction can be +/// completely removed (See ExecuteReject). +#[derive(Debug, Parser)] +pub struct Reject { + #[clap(flatten)] + pub(crate) multisig_account: MultisigAccount, + /// The sequence number of the multisig transaction to reject. The sequence number increments + /// for every new multisig transaction. + #[clap(long)] + pub(crate) sequence_number: u64, + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for Reject { + fn command_name(&self) -> &'static str { + "RejectMultisig" + } + + async fn execute(self) -> CliTypedResult { + self.txn_options + .submit_transaction(aptos_stdlib::multisig_account_reject_transaction( + self.multisig_account.multisig_address, + self.sequence_number, + )) + .await + .map(|inner| inner.into()) + } +} + +/// Execute a proposed multisig transaction. +/// +/// The transaction to be executed needs to have as many approvals as the number of signatures +/// required. +#[derive(Debug, Parser)] +pub struct Execute { + #[clap(flatten)] + pub(crate) multisig_account: MultisigAccount, + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for Execute { + fn command_name(&self) -> &'static str { + "ExecuteMultisig" + } + + async fn execute(self) -> CliTypedResult { + let payload = TransactionPayload::Multisig(Multisig { + multisig_address: self.multisig_account.multisig_address, + // TODO: Support passing an explicit payload + transaction_payload: None, + }); + self.txn_options + .submit_transaction(payload) + .await + .map(|inner| inner.into()) + } +} + +/// Remove a proposed multisig transaction. +/// +/// The transaction to be removed needs to have as many rejections as the number of signatures +/// required. +#[derive(Debug, Parser)] +pub struct ExecuteReject { + #[clap(flatten)] + pub(crate) multisig_account: MultisigAccount, + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for ExecuteReject { + fn command_name(&self) -> &'static str { + "ExecuteRejectMultisig" + } + + async fn execute(self) -> CliTypedResult { + self.txn_options + .submit_transaction(aptos_stdlib::multisig_account_execute_rejected_transaction( + self.multisig_account.multisig_address, + )) + .await + .map(|inner| inner.into()) + } +} diff --git a/movement-sdk/clis/aptos/src/account/transfer.rs b/movement-sdk/clis/aptos/src/account/transfer.rs new file mode 100644 index 000000000..ce8b697dd --- /dev/null +++ b/movement-sdk/clis/aptos/src/account/transfer.rs @@ -0,0 +1,115 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::types::{CliCommand, CliTypedResult, TransactionOptions}; +use aptos_cached_packages::aptos_stdlib; +use aptos_rest_client::{ + aptos_api_types::{HashValue, WriteResource, WriteSetChange}, + Transaction, +}; +use aptos_types::account_address::AccountAddress; +use async_trait::async_trait; +use clap::Parser; +use serde::Serialize; +use std::collections::BTreeMap; + +// TODO: Add ability to transfer non-APT coins +// TODO: Add ability to not create account by default +/// Transfer APT between accounts +/// +#[derive(Debug, Parser)] +pub struct TransferCoins { + /// Address of account to send APT to + #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] + pub(crate) account: AccountAddress, + + /// Amount of Octas (10^-8 APT) to transfer + #[clap(long)] + pub(crate) amount: u64, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for TransferCoins { + fn command_name(&self) -> &'static str { + "TransferCoins" + } + + async fn execute(self) -> CliTypedResult { + self.txn_options + .submit_transaction(aptos_stdlib::aptos_account_transfer( + self.account, + self.amount, + )) + .await + .map(TransferSummary::from) + } +} + +const SUPPORTED_COINS: [&str; 1] = ["0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"]; + +/// A shortened transaction output +#[derive(Clone, Debug, Serialize)] +pub struct TransferSummary { + pub gas_unit_price: u64, + pub gas_used: u64, + pub balance_changes: BTreeMap, + pub sender: AccountAddress, + pub success: bool, + pub version: u64, + pub vm_status: String, + pub transaction_hash: HashValue, +} + +impl TransferSummary { + pub fn octa_spent(&self) -> u64 { + self.gas_unit_price * self.gas_used + } +} + +impl From for TransferSummary { + fn from(transaction: Transaction) -> Self { + if let Transaction::UserTransaction(txn) = transaction { + let vm_status = txn.info.vm_status; + let success = txn.info.success; + let sender = *txn.request.sender.inner(); + let gas_unit_price = txn.request.gas_unit_price.0; + let gas_used = txn.info.gas_used.0; + let transaction_hash = txn.info.hash; + let version = txn.info.version.0; + let balance_changes = txn + .info + .changes + .into_iter() + .filter_map(|change| match change { + WriteSetChange::WriteResource(WriteResource { address, data, .. }) => { + if SUPPORTED_COINS.contains(&data.typ.to_string().as_str()) { + Some(( + *address.inner(), + serde_json::to_value(data.data).unwrap_or_default(), + )) + } else { + None + } + }, + _ => None, + }) + .collect(); + + TransferSummary { + gas_unit_price, + gas_used, + balance_changes, + sender, + success, + version, + vm_status, + transaction_hash, + } + } else { + panic!("Can't call From for a non UserTransaction") + } + } +} diff --git a/movement-sdk/clis/aptos/src/common/init.rs b/movement-sdk/clis/aptos/src/common/init.rs new file mode 100644 index 000000000..9cd60c4b7 --- /dev/null +++ b/movement-sdk/clis/aptos/src/common/init.rs @@ -0,0 +1,374 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + account::key_rotation::lookup_address, + common::{ + types::{ + account_address_from_public_key, CliCommand, CliConfig, CliError, CliTypedResult, + ConfigSearchMode, EncodingOptions, PrivateKeyInputOptions, ProfileConfig, + ProfileOptions, PromptOptions, RngArgs, DEFAULT_PROFILE, + }, + utils::{fund_account, fund_pub_key, prompt_yes_with_override, read_line, wait_for_transactions}, + }, +}; +use aptos_crypto::{ed25519::Ed25519PrivateKey, PrivateKey, ValidCryptoMaterialStringExt}; +use aptos_rest_client::{ + aptos_api_types::{AptosError, AptosErrorCode}, + error::{AptosErrorResponse, RestError}, +}; +use async_trait::async_trait; +use clap::Parser; +use reqwest::Url; +use serde::{Deserialize, Serialize}; +use std::{collections::BTreeMap, str::FromStr}; + +// +const SEED_NODE_1_REST : &str = "https://seed-node1.movementlabs.xyz"; + +/// 1 APT (might not actually get that much, depending on the faucet) +const NUM_DEFAULT_OCTAS: u64 = 100000000; + +/// Tool to initialize current directory for the aptos tool +/// +/// Configuration will be pushed into .aptos/config.yaml +#[derive(Debug, Parser)] +pub struct InitTool { + /// Network to use for default settings + /// + /// If custom `rest_url` and `faucet_url` are wanted, use `custom` + #[clap(long)] + pub network: Option, + + /// URL to a fullnode on the network + #[clap(long)] + pub rest_url: Option, + + /// URL for the Faucet endpoint + #[clap(long)] + pub faucet_url: Option, + + /// Whether to skip the faucet for a non-faucet endpoint + #[clap(long)] + pub skip_faucet: bool, + + #[clap(flatten)] + pub rng_args: RngArgs, + #[clap(flatten)] + pub(crate) private_key_options: PrivateKeyInputOptions, + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, + #[clap(flatten)] + pub(crate) prompt_options: PromptOptions, + #[clap(flatten)] + pub(crate) encoding_options: EncodingOptions, +} + +#[async_trait] +impl CliCommand<()> for InitTool { + fn command_name(&self) -> &'static str { + "MovementInit" + } + + async fn execute(self) -> CliTypedResult<()> { + let mut config = if CliConfig::config_exists(ConfigSearchMode::CurrentDir) { + CliConfig::load(ConfigSearchMode::CurrentDir)? + } else { + CliConfig::default() + }; + + let profile_name = self + .profile_options + .profile_name() + .unwrap_or(DEFAULT_PROFILE); + + // Select profile we're using + let mut profile_config = if let Some(profile_config) = config.remove_profile(profile_name) { + prompt_yes_with_override(&format!("Movement already initialized for profile {}, do you want to overwrite the existing config?", profile_name), self.prompt_options)?; + profile_config + } else { + ProfileConfig::default() + }; + + eprintln!("Configuring for profile {}", profile_name); + + // Choose a network + let network = if let Some(network) = self.network { + eprintln!("Configuring for network {:?}", network); + network + } else { + eprintln!( + "Choose network from [devnet, testnet, mainnet, local, custom | defaults to devnet]" + ); + let input = read_line("network")?; + let input = input.trim(); + if input.is_empty() { + eprintln!("No network given, using devnet..."); + Network::Devnet + } else { + Network::from_str(input)? + } + }; + + // Ensure that there is at least a REST URL set for the network + match network { + Network::Mainnet => { + profile_config.rest_url = + Some(SEED_NODE_1_REST.to_string()); + profile_config.faucet_url = + Some(SEED_NODE_1_REST.to_string()); + }, + Network::Testnet => { + profile_config.rest_url = + Some(SEED_NODE_1_REST.to_string()); + profile_config.faucet_url = + Some(SEED_NODE_1_REST.to_string()); + }, + Network::Devnet => { + profile_config.rest_url = Some(SEED_NODE_1_REST.to_string()); + profile_config.faucet_url = Some(SEED_NODE_1_REST.to_string()); + }, + Network::Local => { + profile_config.rest_url = Some("http://localhost:8080".to_string()); + profile_config.faucet_url = Some("http://localhost:8081".to_string()); + }, + Network::Custom => self.custom_network(&mut profile_config)?, + } + + // Private key + let private_key = if let Some(private_key) = self + .private_key_options + .extract_private_key_cli(self.encoding_options.encoding)? + { + eprintln!("Using command line argument for private key"); + private_key + } else { + eprintln!("Enter your private key as a hex literal (0x...) [Current: {} | No input: Generate new key (or keep one if present)]", profile_config.private_key.as_ref().map(|_| "Redacted").unwrap_or("None")); + let input = read_line("Private key")?; + let input = input.trim(); + if input.is_empty() { + if let Some(private_key) = profile_config.private_key { + eprintln!("No key given, keeping existing key..."); + private_key + } else { + eprintln!("No key given, generating key..."); + self.rng_args + .key_generator()? + .generate_ed25519_private_key() + } + } else { + Ed25519PrivateKey::from_encoded_string(input) + .map_err(|err| CliError::UnableToParse("Ed25519PrivateKey", err.to_string()))? + } + }; + let public_key = private_key.public_key(); + + let client = aptos_rest_client::Client::new( + Url::parse( + profile_config + .rest_url + .as_ref() + .expect("Must have rest client as created above"), + ) + .map_err(|err| CliError::UnableToParse("rest_url", err.to_string()))?, + ); + + // lookup the address from onchain instead of deriving it + // if this is the rotated key, deriving it will outputs an incorrect address + let derived_address = account_address_from_public_key(&public_key); + let address = lookup_address(&client, derived_address, false).await?; + + profile_config.private_key = Some(private_key); + profile_config.public_key = Some(public_key.clone()); + profile_config.account = Some(address); + + // Create account if it doesn't exist (and there's a faucet) + // Check if account exists + let account_exists = match client.get_account(address).await { + Ok(_) => true, + Err(err) => { + if let RestError::Api(AptosErrorResponse { + error: + AptosError { + error_code: AptosErrorCode::ResourceNotFound, + .. + }, + .. + }) + | RestError::Api(AptosErrorResponse { + error: + AptosError { + error_code: AptosErrorCode::AccountNotFound, + .. + }, + .. + }) = err + { + false + } else { + return Err(CliError::UnexpectedError(format!( + "Failed to check if account exists: {:?}", + err + ))); + } + }, + }; + + // If you want to create a private key, but not fund the account, skipping the faucet is still possible + let maybe_faucet_url = if self.skip_faucet { + None + } else { + profile_config.faucet_url.as_ref() + }; + + if let Some(faucet_url) = maybe_faucet_url { + if account_exists { + eprintln!("Account {} has been already found onchain", address); + } else { + eprintln!( + "Account {} doesn't exist, creating it and funding it with {} Octas", + address, NUM_DEFAULT_OCTAS + ); + let hashes = fund_pub_key( + Url::parse(faucet_url) + .map_err(|err| CliError::UnableToParse("rest_url", err.to_string()))?, + // NUM_DEFAULT_OCTAS, + (public_key.clone()).to_string(), + ) + .await?; + wait_for_transactions(&client, hashes).await?; + eprintln!("Account {} funded successfully", address); + } + } else if account_exists { + eprintln!("Account {} has been already found onchain", address); + } else if network == Network::Mainnet { + eprintln!("Account {} does not exist, you will need to create and fund the account by transferring funds from another account", address); + } else { + eprintln!("Account {} has been initialized locally, but you must transfer coins to it to create the account onchain", address); + } + + // Ensure the loaded config has profiles setup for a possible empty file + if config.profiles.is_none() { + config.profiles = Some(BTreeMap::new()); + } + config + .profiles + .as_mut() + .expect("Must have profiles, as created above") + .insert(profile_name.to_string(), profile_config); + config.save()?; + eprintln!("\n---\nMovement CLI is now set up for account {} as profile {}! Run `movement --help` for more information about commands", address, self.profile_options.profile_name().unwrap_or(DEFAULT_PROFILE)); + Ok(()) + } +} + +impl InitTool { + /// Custom network created, which requires a REST URL + fn custom_network(&self, profile_config: &mut ProfileConfig) -> CliTypedResult<()> { + // Rest Endpoint + let rest_url = if let Some(ref rest_url) = self.rest_url { + eprintln!("Using command line argument for rest URL {}", rest_url); + Some(rest_url.to_string()) + } else { + let current = profile_config.rest_url.as_deref(); + eprintln!( + "Enter your rest endpoint [Current: {} | No input: Exit (or keep the existing if present)]", + current.unwrap_or("None"), + ); + let input = read_line("Rest endpoint")?; + let input = input.trim(); + if input.is_empty() { + if let Some(current) = current { + eprintln!("No rest url given, keeping the existing url..."); + Some(current.to_string()) + } else { + eprintln!("No rest url given, exiting..."); + return Err(CliError::AbortedError); + } + } else { + Some( + reqwest::Url::parse(input) + .map_err(|err| CliError::UnableToParse("Rest Endpoint", err.to_string()))? + .to_string(), + ) + } + }; + profile_config.rest_url = rest_url; + + // Faucet Endpoint + let faucet_url = if self.skip_faucet { + eprintln!("Not configuring a faucet because --skip-faucet was provided"); + None + } else if let Some(ref faucet_url) = self.faucet_url { + eprintln!("Using command line argument for faucet URL {}", faucet_url); + Some(faucet_url.to_string()) + } else { + let current = profile_config.faucet_url.as_deref(); + eprintln!( + "Enter your faucet endpoint [Current: {} | No input: Skip (or keep the existing one if present) | 'skip' to not use a faucet]", + current + .unwrap_or("None"), + ); + let input = read_line("Faucet endpoint")?; + let input = input.trim(); + if input.is_empty() { + if let Some(current) = current { + eprintln!("No faucet url given, keeping the existing url..."); + Some(current.to_string()) + } else { + eprintln!("No faucet url given, skipping faucet..."); + None + } + } else if input.to_lowercase() == "skip" { + eprintln!("Skipping faucet..."); + None + } else { + Some( + reqwest::Url::parse(input) + .map_err(|err| CliError::UnableToParse("Faucet Endpoint", err.to_string()))? + .to_string(), + ) + } + }; + profile_config.faucet_url = faucet_url; + Ok(()) + } +} + +/// A simplified list of all networks supported by the CLI +/// +/// Any command using this, will be simpler to setup as profiles +#[derive(Copy, Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +pub enum Network { + Mainnet, + Testnet, + Devnet, + Local, + Custom, +} + +impl FromStr for Network { + type Err = CliError; + + fn from_str(s: &str) -> Result { + Ok(match s.to_lowercase().trim() { + "mainnet" => Self::Mainnet, + "testnet" => Self::Testnet, + "devnet" => Self::Devnet, + "local" => Self::Local, + "custom" => Self::Custom, + str => { + return Err(CliError::CommandArgumentError(format!( + "Invalid network {}. Must be one of [devnet, testnet, mainnet, local, custom]", + str + ))); + }, + }) + } +} + +impl Default for Network { + fn default() -> Self { + Self::Devnet + } +} diff --git a/movement-sdk/clis/aptos/src/common/mod.rs b/movement-sdk/clis/aptos/src/common/mod.rs new file mode 100644 index 000000000..1be94da1d --- /dev/null +++ b/movement-sdk/clis/aptos/src/common/mod.rs @@ -0,0 +1,6 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +pub mod init; +pub mod types; +pub mod utils; diff --git a/movement-sdk/clis/aptos/src/common/types.rs b/movement-sdk/clis/aptos/src/common/types.rs new file mode 100644 index 000000000..bbaf52b0e --- /dev/null +++ b/movement-sdk/clis/aptos/src/common/types.rs @@ -0,0 +1,1753 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{ + init::Network, + utils::{ + check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, + get_account_with_state, get_auth_key, get_sequence_number, prompt_yes_with_override, + read_from_file, start_logger, to_common_result, to_common_success_result, + write_to_file, write_to_file_with_opts, write_to_user_only_file, + }, + }, + config::GlobalConfig, + genesis::git::from_yaml, + move_tool::{ArgWithType, MemberId}, +}; +use aptos_crypto::{ + ed25519::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature}, + x25519, PrivateKey, ValidCryptoMaterial, ValidCryptoMaterialStringExt, +}; +use aptos_debugger::AptosDebugger; +use aptos_gas_profiling::FrameName; +use aptos_global_constants::adjust_gas_headroom; +use aptos_keygen::KeyGen; +use aptos_rest_client::{ + aptos_api_types::{HashValue, MoveType, ViewRequest}, + error::RestError, + Client, Transaction, +}; +use aptos_sdk::{transaction_builder::TransactionFactory, types::LocalAccount}; +use aptos_types::{ + chain_id::ChainId, + transaction::{ + authenticator::AuthenticationKey, EntryFunction, SignedTransaction, TransactionPayload, + TransactionStatus, + }, +}; +use async_trait::async_trait; +use clap::{ArgEnum, Parser}; +use hex::FromHexError; +use move_core_types::{account_address::AccountAddress, language_storage::TypeTag}; +use serde::{Deserialize, Serialize}; +#[cfg(unix)] +use std::os::unix::fs::OpenOptionsExt; +use std::{ + collections::BTreeMap, + convert::TryFrom, + fmt::{Debug, Display, Formatter}, + fs::OpenOptions, + path::{Path, PathBuf}, + str::FromStr, + time::{Duration, Instant, SystemTime, UNIX_EPOCH}, +}; +use reqwest::Url; +use thiserror::Error; + +pub const USER_AGENT: &str = concat!("movement-cli/", env!("CARGO_PKG_VERSION")); +const US_IN_SECS: u64 = 1_000_000; +const ACCEPTED_CLOCK_SKEW_US: u64 = 5 * US_IN_SECS; +pub const DEFAULT_EXPIRATION_SECS: u64 = 60; +pub const DEFAULT_PROFILE: &str = "default"; + +/// A common result to be returned to users +pub type CliResult = Result; + +/// A common result to remove need for typing `Result` +pub type CliTypedResult = Result; + +/// CLI Errors for reporting through telemetry and outputs +#[derive(Debug, Error)] +pub enum CliError { + #[error("Aborted command")] + AbortedError, + #[error("API error: {0}")] + ApiError(String), + #[error("Error (de)serializing '{0}': {1}")] + BCS(&'static str, #[source] bcs::Error), + #[error("Invalid arguments: {0}")] + CommandArgumentError(String), + #[error("Unable to load config: {0} {1}")] + ConfigLoadError(String, String), + #[error("Unable to find config {0}, have you run `movement init`?")] + ConfigNotFoundError(String), + #[error("Error accessing '{0}': {1}")] + IO(String, #[source] std::io::Error), + #[error("Move compilation failed: {0}")] + MoveCompilationError(String), + #[error("Move unit tests failed")] + MoveTestError, + #[error("Move Prover failed: {0}")] + MoveProverError(String), + #[error("Unable to parse '{0}': error: {1}")] + UnableToParse(&'static str, String), + #[error("Unable to read file '{0}', error: {1}")] + UnableToReadFile(String, String), + #[error("Unexpected error: {0}")] + UnexpectedError(String), + #[error("Simulation failed with status: {0}")] + SimulationError(String), + #[error("Coverage failed with status: {0}")] + CoverageError(String), +} + +impl CliError { + pub fn to_str(&self) -> &'static str { + match self { + CliError::AbortedError => "AbortedError", + CliError::ApiError(_) => "ApiError", + CliError::BCS(_, _) => "BCS", + CliError::CommandArgumentError(_) => "CommandArgumentError", + CliError::ConfigLoadError(_, _) => "ConfigLoadError", + CliError::ConfigNotFoundError(_) => "ConfigNotFoundError", + CliError::IO(_, _) => "IO", + CliError::MoveCompilationError(_) => "MoveCompilationError", + CliError::MoveTestError => "MoveTestError", + CliError::MoveProverError(_) => "MoveProverError", + CliError::UnableToParse(_, _) => "UnableToParse", + CliError::UnableToReadFile(_, _) => "UnableToReadFile", + CliError::UnexpectedError(_) => "UnexpectedError", + CliError::SimulationError(_) => "SimulationError", + CliError::CoverageError(_) => "CoverageError", + } + } +} + +impl From for CliError { + fn from(e: RestError) -> Self { + CliError::ApiError(e.to_string()) + } +} + +impl From for CliError { + fn from(e: aptos_config::config::Error) -> Self { + CliError::UnexpectedError(e.to_string()) + } +} + +impl From for CliError { + fn from(e: aptos_github_client::Error) -> Self { + CliError::UnexpectedError(e.to_string()) + } +} + +impl From for CliError { + fn from(e: serde_yaml::Error) -> Self { + CliError::UnexpectedError(e.to_string()) + } +} + +impl From for CliError { + fn from(e: base64::DecodeError) -> Self { + CliError::UnexpectedError(e.to_string()) + } +} + +impl From for CliError { + fn from(e: std::string::FromUtf8Error) -> Self { + CliError::UnexpectedError(e.to_string()) + } +} + +impl From for CliError { + fn from(e: aptos_crypto::CryptoMaterialError) -> Self { + CliError::UnexpectedError(e.to_string()) + } +} + +impl From for CliError { + fn from(e: FromHexError) -> Self { + CliError::UnexpectedError(e.to_string()) + } +} + +impl From for CliError { + fn from(e: anyhow::Error) -> Self { + CliError::UnexpectedError(e.to_string()) + } +} + +impl From for CliError { + fn from(e: bcs::Error) -> Self { + CliError::UnexpectedError(e.to_string()) + } +} + +/// Config saved to `.aptos/config.yaml` +#[derive(Debug, Serialize, Deserialize)] +pub struct CliConfig { + /// Map of profile configs + #[serde(skip_serializing_if = "Option::is_none")] + pub profiles: Option>, +} + +const CONFIG_FILE: &str = "config.yaml"; +const LEGACY_CONFIG_FILE: &str = "config.yml"; +pub const CONFIG_FOLDER: &str = ".movement"; + +/// An individual profile +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct ProfileConfig { + #[serde(skip_serializing_if = "Option::is_none")] + pub network: Option, + /// Private key for commands. + #[serde(skip_serializing_if = "Option::is_none")] + pub private_key: Option, + /// Public key for commands + #[serde(skip_serializing_if = "Option::is_none")] + pub public_key: Option, + /// Account for commands + #[serde(skip_serializing_if = "Option::is_none")] + pub account: Option, + /// URL for the Aptos rest endpoint + #[serde(skip_serializing_if = "Option::is_none")] + pub rest_url: Option, + /// URL for the Faucet endpoint (if applicable) + #[serde(skip_serializing_if = "Option::is_none")] + pub faucet_url: Option, +} + +/// ProfileConfig but without the private parts +#[derive(Debug, Serialize)] +pub struct ProfileSummary { + pub has_private_key: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub public_key: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub account: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub rest_url: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub faucet_url: Option, +} + +impl From<&ProfileConfig> for ProfileSummary { + fn from(config: &ProfileConfig) -> Self { + ProfileSummary { + has_private_key: config.private_key.is_some(), + public_key: config.public_key.clone(), + account: config.account, + rest_url: config.rest_url.clone(), + faucet_url: config.faucet_url.clone(), + } + } +} + +impl Default for CliConfig { + fn default() -> Self { + CliConfig { + profiles: Some(BTreeMap::new()), + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] +pub enum ConfigSearchMode { + CurrentDir, + CurrentDirAndParents, +} + +impl CliConfig { + /// Checks if the config exists in the current working directory + pub fn config_exists(mode: ConfigSearchMode) -> bool { + if let Ok(folder) = Self::aptos_folder(mode) { + let config_file = folder.join(CONFIG_FILE); + let old_config_file = folder.join(LEGACY_CONFIG_FILE); + config_file.exists() || old_config_file.exists() + } else { + false + } + } + + /// Loads the config from the current working directory or one of its parents. + pub fn load(mode: ConfigSearchMode) -> CliTypedResult { + let folder = Self::aptos_folder(mode)?; + + let config_file = folder.join(CONFIG_FILE); + let old_config_file = folder.join(LEGACY_CONFIG_FILE); + if config_file.exists() { + from_yaml( + &String::from_utf8(read_from_file(config_file.as_path())?) + .map_err(CliError::from)?, + ) + } else if old_config_file.exists() { + from_yaml( + &String::from_utf8(read_from_file(old_config_file.as_path())?) + .map_err(CliError::from)?, + ) + } else { + Err(CliError::ConfigNotFoundError(format!( + "{}", + config_file.display() + ))) + } + } + + pub fn load_profile( + profile: Option<&str>, + mode: ConfigSearchMode, + ) -> CliTypedResult> { + let mut config = Self::load(mode)?; + + // If no profile was given, use `default` + if let Some(profile) = profile { + if let Some(account_profile) = config.remove_profile(profile) { + Ok(Some(account_profile)) + } else { + Err(CliError::CommandArgumentError(format!( + "Profile {} not found", + profile + ))) + } + } else { + Ok(config.remove_profile(DEFAULT_PROFILE)) + } + } + + pub fn remove_profile(&mut self, profile: &str) -> Option { + if let Some(ref mut profiles) = self.profiles { + profiles.remove(&profile.to_string()) + } else { + None + } + } + + /// Saves the config to ./.aptos/config.yaml + pub fn save(&self) -> CliTypedResult<()> { + let aptos_folder = Self::aptos_folder(ConfigSearchMode::CurrentDir)?; + + // Create if it doesn't exist + create_dir_if_not_exist(aptos_folder.as_path())?; + + // Save over previous config file + let config_file = aptos_folder.join(CONFIG_FILE); + let config_bytes = serde_yaml::to_string(&self).map_err(|err| { + CliError::UnexpectedError(format!("Failed to serialize config {}", err)) + })?; + write_to_user_only_file(&config_file, CONFIG_FILE, config_bytes.as_bytes())?; + + // As a cleanup, delete the old if it exists + let legacy_config_file = aptos_folder.join(LEGACY_CONFIG_FILE); + if legacy_config_file.exists() { + eprintln!("Removing legacy config file {}", LEGACY_CONFIG_FILE); + let _ = std::fs::remove_file(legacy_config_file); + } + Ok(()) + } + + /// Finds the current directory's .aptos folder + fn aptos_folder(mode: ConfigSearchMode) -> CliTypedResult { + let global_config = GlobalConfig::load()?; + global_config.get_config_location(mode) + } +} + +/// Types of Keys used by the blockchain +#[derive(ArgEnum, Clone, Copy, Debug)] +pub enum KeyType { + /// Ed25519 key used for signing + Ed25519, + /// X25519 key used for network handshakes and identity + X25519, + /// A BLS12381 key for consensus + Bls12381, +} + +impl Display for KeyType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let str = match self { + KeyType::Ed25519 => "ed25519", + KeyType::X25519 => "x25519", + KeyType::Bls12381 => "bls12381", + }; + write!(f, "{}", str) + } +} + +impl FromStr for KeyType { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "ed25519" => Ok(KeyType::Ed25519), + "x25519" => Ok(KeyType::X25519), + "bls12381" => Ok(KeyType::Bls12381), + _ => Err("Invalid key type: Must be one of [ed25519, x25519]"), + } + } +} + +#[derive(Debug, Default, Parser)] +pub struct ProfileOptions { + /// Profile to use from the CLI config + /// + /// This will be used to override associated settings such as + /// the REST URL, the Faucet URL, and the private key arguments. + /// + /// Defaults to "default" + #[clap(long)] + pub profile: Option, +} + +impl ProfileOptions { + pub fn account_address(&self) -> CliTypedResult { + let profile = self.profile()?; + if let Some(account) = profile.account { + return Ok(account); + } + + Err(CliError::ConfigNotFoundError( + self.profile + .clone() + .unwrap_or_else(|| DEFAULT_PROFILE.to_string()), + )) + } + + pub fn public_key(&self) -> CliTypedResult { + let profile = self.profile()?; + if let Some(public_key) = profile.public_key { + return Ok(public_key); + } + + Err(CliError::ConfigNotFoundError( + self.profile + .clone() + .unwrap_or_else(|| DEFAULT_PROFILE.to_string()), + )) + } + + pub fn profile_name(&self) -> Option<&str> { + self.profile.as_ref().map(|inner| inner.trim()) + } + + pub fn profile(&self) -> CliTypedResult { + if let Some(profile) = + CliConfig::load_profile(self.profile_name(), ConfigSearchMode::CurrentDirAndParents)? + { + return Ok(profile); + } + + Err(CliError::ConfigNotFoundError( + self.profile + .clone() + .unwrap_or_else(|| DEFAULT_PROFILE.to_string()), + )) + } +} + +/// Types of encodings used by the blockchain +#[derive(ArgEnum, Clone, Copy, Debug)] +pub enum EncodingType { + /// Binary Canonical Serialization + BCS, + /// Hex encoded e.g. 0xABCDE12345 + Hex, + /// Base 64 encoded + Base64, +} + +impl EncodingType { + /// Encodes `Key` into one of the `EncodingType`s + pub fn encode_key( + &self, + name: &'static str, + key: &Key, + ) -> CliTypedResult> { + Ok(match self { + EncodingType::Hex => hex::encode_upper(key.to_bytes()).into_bytes(), + EncodingType::BCS => bcs::to_bytes(key).map_err(|err| CliError::BCS(name, err))?, + EncodingType::Base64 => base64::encode(key.to_bytes()).into_bytes(), + }) + } + + /// Loads a key from a file + pub fn load_key( + &self, + name: &'static str, + path: &Path, + ) -> CliTypedResult { + self.decode_key(name, read_from_file(path)?) + } + + /// Decodes an encoded key given the known encoding + pub fn decode_key( + &self, + name: &'static str, + data: Vec, + ) -> CliTypedResult { + match self { + EncodingType::BCS => bcs::from_bytes(&data).map_err(|err| CliError::BCS(name, err)), + EncodingType::Hex => { + let hex_string = String::from_utf8(data)?; + Key::from_encoded_string(hex_string.trim()) + .map_err(|err| CliError::UnableToParse(name, err.to_string())) + } + EncodingType::Base64 => { + let string = String::from_utf8(data)?; + let bytes = base64::decode(string.trim()) + .map_err(|err| CliError::UnableToParse(name, err.to_string()))?; + Key::try_from(bytes.as_slice()).map_err(|err| { + CliError::UnableToParse(name, format!("Failed to parse key {:?}", err)) + }) + } + } + } +} + +#[derive(Clone, Debug, Parser)] +pub struct RngArgs { + /// The seed used for key generation, should be a 64 character hex string and only used for testing + /// + /// If a predictable random seed is used, the key that is produced will be insecure and easy + /// to reproduce. Please do not use this unless sufficient randomness is put into the random + /// seed. + #[clap(long)] + random_seed: Option, +} + +impl RngArgs { + pub fn from_seed(seed: [u8; 32]) -> RngArgs { + RngArgs { + random_seed: Some(hex::encode(seed)), + } + } + + pub fn from_string_seed(str: &str) -> RngArgs { + assert!(str.len() < 32); + + let mut seed = [0u8; 32]; + for (i, byte) in str.bytes().enumerate() { + seed[i] = byte; + } + + RngArgs { + random_seed: Some(hex::encode(seed)), + } + } + + /// Returns a key generator with the seed if given + pub fn key_generator(&self) -> CliTypedResult { + if let Some(ref seed) = self.random_seed { + // Strip 0x + let seed = seed.strip_prefix("0x").unwrap_or(seed); + let mut seed_slice = [0u8; 32]; + + hex::decode_to_slice(seed, &mut seed_slice)?; + Ok(KeyGen::from_seed(seed_slice)) + } else { + Ok(KeyGen::from_os_rng()) + } + } +} + +impl Default for EncodingType { + fn default() -> Self { + EncodingType::Hex + } +} + +impl Display for EncodingType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let str = match self { + EncodingType::BCS => "bcs", + EncodingType::Hex => "hex", + EncodingType::Base64 => "base64", + }; + write!(f, "{}", str) + } +} + +impl FromStr for EncodingType { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "hex" => Ok(EncodingType::Hex), + "bcs" => Ok(EncodingType::BCS), + "base64" => Ok(EncodingType::Base64), + _ => Err("Invalid encoding type"), + } + } +} + +/// An insertable option for use with prompts. +#[derive(Clone, Copy, Debug, Default, Parser, PartialEq, Eq)] +pub struct PromptOptions { + /// Assume yes for all yes/no prompts + #[clap(long, group = "prompt_options")] + pub assume_yes: bool, + /// Assume no for all yes/no prompts + #[clap(long, group = "prompt_options")] + pub assume_no: bool, +} + +impl PromptOptions { + pub fn yes() -> Self { + Self { + assume_yes: true, + assume_no: false, + } + } + + pub fn no() -> Self { + Self { + assume_yes: false, + assume_no: true, + } + } +} + +/// An insertable option for use with encodings. +#[derive(Debug, Default, Parser)] +pub struct EncodingOptions { + /// Encoding of data as one of [base64, bcs, hex] + #[clap(long, default_value_t = EncodingType::Hex)] + pub encoding: EncodingType, +} + +#[derive(Debug, Parser)] +pub struct PublicKeyInputOptions { + /// Ed25519 Public key input file name + /// + /// Mutually exclusive with `--public-key` + #[clap(long, group = "public_key_input", parse(from_os_str))] + public_key_file: Option, + /// Ed25519 Public key encoded in a type as shown in `encoding` + /// + /// Mutually exclusive with `--public-key-file` + #[clap(long, group = "public_key_input")] + public_key: Option, +} + +impl PublicKeyInputOptions { + pub fn from_key(key: &Ed25519PublicKey) -> PublicKeyInputOptions { + PublicKeyInputOptions { + public_key: Some(key.to_encoded_string().unwrap()), + public_key_file: None, + } + } +} + +impl ExtractPublicKey for PublicKeyInputOptions { + fn extract_public_key( + &self, + encoding: EncodingType, + profile: &ProfileOptions, + ) -> CliTypedResult { + if let Some(ref file) = self.public_key_file { + encoding.load_key("--public-key-file", file.as_path()) + } else if let Some(ref key) = self.public_key { + let key = key.as_bytes().to_vec(); + encoding.decode_key("--public-key", key) + } else if let Some(Some(public_key)) = CliConfig::load_profile( + profile.profile_name(), + ConfigSearchMode::CurrentDirAndParents, + )? + .map(|p| p.public_key) + { + Ok(public_key) + } else { + Err(CliError::CommandArgumentError( + "One of ['--public-key', '--public-key-file', '--profile'] must be used" + .to_string(), + )) + } + } +} + +pub trait ParsePrivateKey { + fn parse_private_key( + &self, + encoding: EncodingType, + private_key_file: Option, + private_key: Option, + ) -> CliTypedResult> { + if let Some(ref file) = private_key_file { + Ok(Some( + encoding.load_key("--private-key-file", file.as_path())?, + )) + } else if let Some(ref key) = private_key { + let key = key.as_bytes().to_vec(); + Ok(Some(encoding.decode_key("--private-key", key)?)) + } else { + Ok(None) + } + } +} + +#[derive(Debug, Default, Parser)] +pub struct PrivateKeyInputOptions { + /// Signing Ed25519 private key file path + /// + /// Encoded with type from `--encoding` + /// Mutually exclusive with `--private-key` + #[clap(long, group = "private_key_input", parse(from_os_str))] + private_key_file: Option, + /// Signing Ed25519 private key + /// + /// Encoded with type from `--encoding` + /// Mutually exclusive with `--private-key-file` + #[clap(long, group = "private_key_input")] + private_key: Option, +} + +impl ParsePrivateKey for PrivateKeyInputOptions {} + +impl PrivateKeyInputOptions { + pub fn from_private_key(private_key: &Ed25519PrivateKey) -> CliTypedResult { + Ok(PrivateKeyInputOptions { + private_key: Some( + private_key + .to_encoded_string() + .map_err(|err| CliError::UnexpectedError(err.to_string()))?, + ), + private_key_file: None, + }) + } + + pub fn from_x25519_private_key(private_key: &x25519::PrivateKey) -> CliTypedResult { + Ok(PrivateKeyInputOptions { + private_key: Some( + private_key + .to_encoded_string() + .map_err(|err| CliError::UnexpectedError(err.to_string()))?, + ), + private_key_file: None, + }) + } + + pub fn from_file(file: PathBuf) -> Self { + PrivateKeyInputOptions { + private_key: None, + private_key_file: Some(file), + } + } + + /// Extract private key from CLI args with fallback to config + pub fn extract_private_key_and_address( + &self, + encoding: EncodingType, + profile: &ProfileOptions, + maybe_address: Option, + ) -> CliTypedResult<(Ed25519PrivateKey, AccountAddress)> { + // Order of operations + // 1. CLI inputs + // 2. Profile + // 3. Derived + if let Some(key) = self.extract_private_key_cli(encoding)? { + // If we use the CLI inputs, then we should derive or use the address from the input + if let Some(address) = maybe_address { + Ok((key, address)) + } else { + let address = account_address_from_public_key(&key.public_key()); + Ok((key, address)) + } + } else if let Some((Some(key), maybe_config_address)) = CliConfig::load_profile( + profile.profile_name(), + ConfigSearchMode::CurrentDirAndParents, + )? + .map(|p| (p.private_key, p.account)) + { + match (maybe_address, maybe_config_address) { + (Some(address), _) => Ok((key, address)), + (_, Some(address)) => Ok((key, address)), + (None, None) => { + let address = account_address_from_public_key(&key.public_key()); + Ok((key, address)) + } + } + } else { + Err(CliError::CommandArgumentError( + "One of ['--private-key', '--private-key-file'] must be used".to_string(), + )) + } + } + + /// Extract private key from CLI args with fallback to config + pub fn extract_private_key( + &self, + encoding: EncodingType, + profile: &ProfileOptions, + ) -> CliTypedResult { + if let Some(key) = self.extract_private_key_cli(encoding)? { + Ok(key) + } else if let Some(Some(private_key)) = CliConfig::load_profile( + profile.profile_name(), + ConfigSearchMode::CurrentDirAndParents, + )? + .map(|p| p.private_key) + { + Ok(private_key) + } else { + Err(CliError::CommandArgumentError( + "One of ['--private-key', '--private-key-file'] must be used".to_string(), + )) + } + } + + /// Extract private key from CLI args + pub fn extract_private_key_cli( + &self, + encoding: EncodingType, + ) -> CliTypedResult> { + self.parse_private_key( + encoding, + self.private_key_file.clone(), + self.private_key.clone(), + ) + } +} + +impl ExtractPublicKey for PrivateKeyInputOptions { + fn extract_public_key( + &self, + encoding: EncodingType, + profile: &ProfileOptions, + ) -> CliTypedResult { + self.extract_private_key(encoding, profile) + .map(|private_key| private_key.public_key()) + } +} + +pub trait ExtractPublicKey { + fn extract_public_key( + &self, + encoding: EncodingType, + profile: &ProfileOptions, + ) -> CliTypedResult; +} + +pub fn account_address_from_public_key(public_key: &Ed25519PublicKey) -> AccountAddress { + let auth_key = AuthenticationKey::ed25519(public_key); + AccountAddress::new(*auth_key.derived_address()) +} + +#[derive(Debug, Parser)] +pub struct SaveFile { + /// Output file path + #[clap(long, parse(from_os_str))] + pub output_file: PathBuf, + + #[clap(flatten)] + pub prompt_options: PromptOptions, +} + +impl SaveFile { + /// Check if the key file exists already + pub fn check_file(&self) -> CliTypedResult<()> { + check_if_file_exists(self.output_file.as_path(), self.prompt_options) + } + + /// Save to the `output_file` + pub fn save_to_file(&self, name: &str, bytes: &[u8]) -> CliTypedResult<()> { + write_to_file(self.output_file.as_path(), name, bytes) + } + + /// Save to the `output_file` with restricted permissions (mode 0600) + pub fn save_to_file_confidential(&self, name: &str, bytes: &[u8]) -> CliTypedResult<()> { + let mut opts = OpenOptions::new(); + #[cfg(unix)] + opts.mode(0o600); + write_to_file_with_opts(self.output_file.as_path(), name, bytes, &mut opts) + } +} + +/// Options specific to using the Rest endpoint +#[derive(Debug, Default, Parser)] +pub struct RestOptions { + /// URL to a fullnode on the network + /// + /// Defaults to the URL in the `default` profile + #[clap(long)] + pub(crate) url: Option, + + /// Connection timeout in seconds, used for the REST endpoint of the fullnode + #[clap(long, default_value_t = DEFAULT_EXPIRATION_SECS, alias = "connection-timeout-s")] + pub connection_timeout_secs: u64, +} + +impl RestOptions { + pub fn new(url: Option, connection_timeout_secs: Option) -> Self { + RestOptions { + url, + connection_timeout_secs: connection_timeout_secs.unwrap_or(DEFAULT_EXPIRATION_SECS), + } + } + + /// Retrieve the URL from the profile or the command line + pub fn url(&self, profile: &ProfileOptions) -> CliTypedResult { + if let Some(ref url) = self.url { + Ok(url.clone()) + } else if let Some(Some(url)) = CliConfig::load_profile( + profile.profile_name(), + ConfigSearchMode::CurrentDirAndParents, + )? + .map(|p| p.rest_url) + { + reqwest::Url::parse(&url) + .map_err(|err| CliError::UnableToParse("Rest URL", err.to_string())) + } else { + Err(CliError::CommandArgumentError("No rest url given. Please add --url or add a rest_url to the .movement/config.yaml for the current profile".to_string())) + } + } + + pub fn client(&self, profile: &ProfileOptions) -> CliTypedResult { + Ok(Client::new_with_timeout_and_user_agent( + self.url(profile)?, + Duration::from_secs(self.connection_timeout_secs), + USER_AGENT, + )) + } + pub fn client_raw(&self, url: Url) -> CliTypedResult { + Ok(Client::new_with_timeout_and_user_agent( + url, + Duration::from_secs(self.connection_timeout_secs), + USER_AGENT, + )) + } +} + +/// Options for compiling a move package dir +#[derive(Debug, Clone, Parser)] +pub struct MovePackageDir { + /// Path to a move package (the folder with a Move.toml file) + #[clap(long, parse(from_os_str))] + pub package_dir: Option, + /// Path to save the compiled move package + /// + /// Defaults to `/build` + #[clap(long, parse(from_os_str))] + pub output_dir: Option, + /// Named addresses for the move binary + /// + /// Example: alice=0x1234, bob=0x5678 + /// + /// Note: This will fail if there are duplicates in the Move.toml file remove those first. + #[clap(long, parse(try_from_str = crate::common::utils::parse_map), default_value = "")] + pub(crate) named_addresses: BTreeMap, + + /// Skip pulling the latest git dependencies + /// + /// If you don't have a network connection, the compiler may fail due + /// to no ability to pull git dependencies. This will allow overriding + /// this for local development. + #[clap(long)] + pub(crate) skip_fetch_latest_git_deps: bool, + + /// Specify the version of the bytecode the compiler is going to emit. + #[clap(long)] + pub bytecode_version: Option, +} + +impl MovePackageDir { + pub fn new(package_dir: PathBuf) -> Self { + Self { + package_dir: Some(package_dir), + output_dir: None, + named_addresses: Default::default(), + skip_fetch_latest_git_deps: true, + bytecode_version: None, + } + } + + pub fn get_package_path(&self) -> CliTypedResult { + dir_default_to_current(self.package_dir.clone()) + } + + /// Retrieve the NamedAddresses, resolving all the account addresses accordingly + pub fn named_addresses(&self) -> BTreeMap { + self.named_addresses + .clone() + .into_iter() + .map(|(key, value)| (key, value.account_address)) + .collect() + } + + pub fn add_named_address(&mut self, key: String, value: String) { + self.named_addresses + .insert(key, AccountAddressWrapper::from_str(&value).unwrap()); + } +} + +/// A wrapper around `AccountAddress` to be more flexible from strings than AccountAddress +#[derive(Clone, Copy, Debug)] +pub struct AccountAddressWrapper { + pub account_address: AccountAddress, +} + +impl FromStr for AccountAddressWrapper { + type Err = CliError; + + fn from_str(s: &str) -> Result { + Ok(AccountAddressWrapper { + account_address: load_account_arg(s)?, + }) + } +} + +/// Loads an account arg and allows for naming based on profiles +pub fn load_account_arg(str: &str) -> Result { + if str.starts_with("0x") { + AccountAddress::from_hex_literal(str).map_err(|err| { + CliError::CommandArgumentError(format!("Failed to parse AccountAddress {}", err)) + }) + } else if let Ok(account_address) = AccountAddress::from_str(str) { + Ok(account_address) + } else if let Some(Some(account_address)) = + CliConfig::load_profile(Some(str), ConfigSearchMode::CurrentDirAndParents)? + .map(|p| p.account) + { + Ok(account_address) + } else if let Some(Some(private_key)) = + CliConfig::load_profile(Some(str), ConfigSearchMode::CurrentDirAndParents)? + .map(|p| p.private_key) + { + let public_key = private_key.public_key(); + Ok(account_address_from_public_key(&public_key)) + } else { + Err(CliError::CommandArgumentError( + "'--account' or '--profile' after using movement init must be provided".to_string(), + )) + } +} + +/// A wrapper around `AccountAddress` to allow for "_" +#[derive(Clone, Copy, Debug)] +pub struct MoveManifestAccountWrapper { + pub account_address: Option, +} + +impl FromStr for MoveManifestAccountWrapper { + type Err = CliError; + + fn from_str(s: &str) -> Result { + Ok(MoveManifestAccountWrapper { + account_address: load_manifest_account_arg(s)?, + }) + } +} + +/// Loads an account arg and allows for naming based on profiles and "_" +pub fn load_manifest_account_arg(str: &str) -> Result, CliError> { + if str == "_" { + Ok(None) + } else if str.starts_with("0x") { + AccountAddress::from_hex_literal(str) + .map(Some) + .map_err(|err| { + CliError::CommandArgumentError(format!("Failed to parse AccountAddress {}", err)) + }) + } else if let Ok(account_address) = AccountAddress::from_str(str) { + Ok(Some(account_address)) + } else if let Some(Some(private_key)) = + CliConfig::load_profile(Some(str), ConfigSearchMode::CurrentDirAndParents)? + .map(|p| p.private_key) + { + let public_key = private_key.public_key(); + Ok(Some(account_address_from_public_key(&public_key))) + } else { + Err(CliError::CommandArgumentError( + "Invalid Move manifest account address".to_string(), + )) + } +} + +/// A common trait for all CLI commands to have consistent outputs +#[async_trait] +pub trait CliCommand: Sized + Send { + /// Returns a name for logging purposes + fn command_name(&self) -> &'static str; + + /// Executes the command, returning a command specific type + async fn execute(self) -> CliTypedResult; + + /// Executes the command, and serializes it to the common JSON output type + async fn execute_serialized(self) -> CliResult { + let command_name = self.command_name(); + start_logger(); + let start_time = Instant::now(); + to_common_result(command_name, start_time, self.execute().await).await + } + + /// Same as execute serialized without setting up logging + async fn execute_serialized_without_logger(self) -> CliResult { + let command_name = self.command_name(); + let start_time = Instant::now(); + to_common_result(command_name, start_time, self.execute().await).await + } + + /// Executes the command, and throws away Ok(result) for the string Success + async fn execute_serialized_success(self) -> CliResult { + start_logger(); + let command_name = self.command_name(); + let start_time = Instant::now(); + to_common_success_result(command_name, start_time, self.execute().await).await + } +} + +/// A shortened transaction output +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct TransactionSummary { + pub transaction_hash: HashValue, + #[serde(skip_serializing_if = "Option::is_none")] + pub gas_used: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub gas_unit_price: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub pending: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub sender: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub sequence_number: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub success: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub timestamp_us: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub version: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub vm_status: Option, +} + +impl From for TransactionSummary { + fn from(transaction: Transaction) -> Self { + TransactionSummary::from(&transaction) + } +} + +impl From<&Transaction> for TransactionSummary { + fn from(transaction: &Transaction) -> Self { + match transaction { + Transaction::PendingTransaction(txn) => TransactionSummary { + transaction_hash: txn.hash, + pending: Some(true), + sender: Some(*txn.request.sender.inner()), + sequence_number: Some(txn.request.sequence_number.0), + gas_used: None, + gas_unit_price: None, + success: None, + version: None, + vm_status: None, + timestamp_us: None, + }, + Transaction::UserTransaction(txn) => TransactionSummary { + transaction_hash: txn.info.hash, + sender: Some(*txn.request.sender.inner()), + gas_used: Some(txn.info.gas_used.0), + gas_unit_price: Some(txn.request.gas_unit_price.0), + success: Some(txn.info.success), + version: Some(txn.info.version.0), + vm_status: Some(txn.info.vm_status.clone()), + sequence_number: Some(txn.request.sequence_number.0), + timestamp_us: Some(txn.timestamp.0), + pending: None, + }, + Transaction::GenesisTransaction(txn) => TransactionSummary { + transaction_hash: txn.info.hash, + success: Some(txn.info.success), + version: Some(txn.info.version.0), + vm_status: Some(txn.info.vm_status.clone()), + sender: None, + gas_used: None, + gas_unit_price: None, + pending: None, + sequence_number: None, + timestamp_us: None, + }, + Transaction::BlockMetadataTransaction(txn) => TransactionSummary { + transaction_hash: txn.info.hash, + success: Some(txn.info.success), + version: Some(txn.info.version.0), + vm_status: Some(txn.info.vm_status.clone()), + timestamp_us: Some(txn.timestamp.0), + sender: None, + gas_used: None, + gas_unit_price: None, + pending: None, + sequence_number: None, + }, + Transaction::StateCheckpointTransaction(txn) => TransactionSummary { + transaction_hash: txn.info.hash, + success: Some(txn.info.success), + version: Some(txn.info.version.0), + vm_status: Some(txn.info.vm_status.clone()), + timestamp_us: Some(txn.timestamp.0), + sender: None, + gas_used: None, + gas_unit_price: None, + pending: None, + sequence_number: None, + }, + } + } +} + +/// A summary of a `WriteSetChange` for easy printing +#[derive(Clone, Debug, Default, Serialize)] +pub struct ChangeSummary { + #[serde(skip_serializing_if = "Option::is_none")] + address: Option, + #[serde(skip_serializing_if = "Option::is_none")] + data: Option, + event: &'static str, + #[serde(skip_serializing_if = "Option::is_none")] + handle: Option, + #[serde(skip_serializing_if = "Option::is_none")] + key: Option, + #[serde(skip_serializing_if = "Option::is_none")] + module: Option, + #[serde(skip_serializing_if = "Option::is_none")] + resource: Option, + #[serde(skip_serializing_if = "Option::is_none")] + value: Option, +} + +#[derive(Debug, Default, Parser)] +pub struct FaucetOptions { + /// URL for the faucet endpoint e.g. `https://faucet.devnet.aptoslabs.com` + #[clap(long)] + faucet_url: Option, +} + +impl FaucetOptions { + pub fn new(faucet_url: Option) -> Self { + FaucetOptions { faucet_url } + } + + pub fn faucet_url(&self, profile: &ProfileOptions) -> CliTypedResult { + if let Some(ref faucet_url) = self.faucet_url { + Ok(faucet_url.clone()) + } else if let Some(Some(url)) = CliConfig::load_profile( + profile.profile_name(), + ConfigSearchMode::CurrentDirAndParents, + )? + .map(|profile| profile.faucet_url) + { + reqwest::Url::parse(&url) + .map_err(|err| CliError::UnableToParse("config faucet_url", err.to_string())) + } else { + Err(CliError::CommandArgumentError("No faucet given. Please add --faucet-url or add a faucet URL to the .movement/config.yaml for the current profile".to_string())) + } + } +} + +/// Gas price options for manipulating how to prioritize transactions +#[derive(Debug, Eq, Parser, PartialEq)] +pub struct GasOptions { + /// Gas multiplier per unit of gas + /// + /// The amount of Octas (10^-8 APT) used for a transaction is equal + /// to (gas unit price * gas used). The gas_unit_price can + /// be used as a multiplier for the amount of Octas willing + /// to be paid for a transaction. This will prioritize the + /// transaction with a higher gas unit price. + /// + /// Without a value, it will determine the price based on the current estimated price + #[clap(long)] + pub gas_unit_price: Option, + /// Maximum amount of gas units to be used to send this transaction + /// + /// The maximum amount of gas units willing to pay for the transaction. + /// This is the (max gas in Octas / gas unit price). + /// + /// For example if I wanted to pay a maximum of 100 Octas, I may have the + /// max gas set to 100 if the gas unit price is 1. If I want it to have a + /// gas unit price of 2, the max gas would need to be 50 to still only have + /// a maximum price of 100 Octas. + /// + /// Without a value, it will determine the price based on simulating the current transaction + #[clap(long)] + pub max_gas: Option, + /// Number of seconds to expire the transaction + /// + /// This is the number of seconds from the current local computer time. + #[clap(long, default_value_t = DEFAULT_EXPIRATION_SECS)] + pub expiration_secs: u64, +} + +impl Default for GasOptions { + fn default() -> Self { + GasOptions { + gas_unit_price: None, + max_gas: None, + expiration_secs: DEFAULT_EXPIRATION_SECS, + } + } +} + +/// Common options for interacting with an account for a validator +#[derive(Debug, Default, Parser)] +pub struct TransactionOptions { + /// Sender account address + /// + /// This allows you to override the account address from the derived account address + /// in the event that the authentication key was rotated or for a resource account + #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] + pub(crate) sender_account: Option, + + #[clap(flatten)] + pub(crate) private_key_options: PrivateKeyInputOptions, + #[clap(flatten)] + pub(crate) encoding_options: EncodingOptions, + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, + #[clap(flatten)] + pub(crate) rest_options: RestOptions, + #[clap(flatten)] + pub(crate) gas_options: GasOptions, + #[clap(flatten)] + pub(crate) prompt_options: PromptOptions, + + /// If this option is set, simulate the transaction locally using the debugger and generate + /// flamegraphs that reflect the gas usage. + #[clap(long)] + pub(crate) profile_gas: bool, +} + +impl TransactionOptions { + /// Builds a rest client + fn rest_client(&self) -> CliTypedResult { + self.rest_options.client(&self.profile_options) + } + + /// Retrieves the public key and the associated address + /// TODO: Cache this information + pub fn get_key_and_address(&self) -> CliTypedResult<(Ed25519PrivateKey, AccountAddress)> { + self.private_key_options.extract_private_key_and_address( + self.encoding_options.encoding, + &self.profile_options, + self.sender_account, + ) + } + + pub fn sender_address(&self) -> CliTypedResult { + Ok(self.get_key_and_address()?.1) + } + + /// Gets the auth key by account address. We need to fetch the auth key from Rest API rather than creating an + /// auth key out of the public key. + pub(crate) async fn auth_key( + &self, + sender_address: AccountAddress, + ) -> CliTypedResult { + let client = self.rest_client()?; + get_auth_key(&client, sender_address).await + } + + pub async fn sequence_number(&self, sender_address: AccountAddress) -> CliTypedResult { + let client = self.rest_client()?; + get_sequence_number(&client, sender_address).await + } + + pub async fn view(&self, payload: ViewRequest) -> CliTypedResult> { + let client = self.rest_client()?; + Ok(client.view(&payload, None).await?.into_inner()) + } + + /// Submit a transaction + pub async fn submit_transaction( + &self, + payload: TransactionPayload, + ) -> CliTypedResult { + let client = self.rest_client()?; + let (sender_key, sender_address) = self.get_key_and_address()?; + + // Ask to confirm price if the gas unit price is estimated above the lowest value when + // it is automatically estimated + let ask_to_confirm_price; + let gas_unit_price = if let Some(gas_unit_price) = self.gas_options.gas_unit_price { + ask_to_confirm_price = false; + gas_unit_price + } else { + let gas_unit_price = client.estimate_gas_price().await?.into_inner().gas_estimate; + + ask_to_confirm_price = true; + gas_unit_price + }; + + // Get sequence number for account + let (account, state) = get_account_with_state(&client, sender_address).await?; + let sequence_number = account.sequence_number; + + // Retrieve local time, and ensure it's within an expected skew of the blockchain + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|err| CliError::UnexpectedError(err.to_string()))? + .as_secs(); + let now_usecs = now * US_IN_SECS; + + // Warn local user that clock is skewed behind the blockchain. + // There will always be a little lag from real time to blockchain time + if now_usecs < state.timestamp_usecs - ACCEPTED_CLOCK_SKEW_US { + eprintln!("Local clock is is skewed from blockchain clock. Clock is more than {} seconds behind the blockchain {}", ACCEPTED_CLOCK_SKEW_US, state.timestamp_usecs / US_IN_SECS); + } + let expiration_time_secs = now + self.gas_options.expiration_secs; + + let chain_id = ChainId::new(state.chain_id); + // TODO: Check auth key against current private key and provide a better message + + let max_gas = if let Some(max_gas) = self.gas_options.max_gas { + // If the gas unit price was estimated ask, but otherwise you've chosen hwo much you want to spend + if ask_to_confirm_price { + let message = format!("Do you want to submit transaction for a maximum of {} Octas at a gas unit price of {} Octas?", max_gas * gas_unit_price, gas_unit_price); + prompt_yes_with_override(&message, self.prompt_options)?; + } + max_gas + } else { + let transaction_factory = + TransactionFactory::new(chain_id).with_gas_unit_price(gas_unit_price); + + let unsigned_transaction = transaction_factory + .payload(payload.clone()) + .sender(sender_address) + .sequence_number(sequence_number) + .expiration_timestamp_secs(expiration_time_secs) + .build(); + + let signed_transaction = SignedTransaction::new( + unsigned_transaction, + sender_key.public_key(), + Ed25519Signature::try_from([0u8; 64].as_ref()).unwrap(), + ); + + let txns = client + .simulate_with_gas_estimation(&signed_transaction, true, false) + .await? + .into_inner(); + let simulated_txn = txns.first().unwrap(); + + // Check if the transaction will pass, if it doesn't then fail + if !simulated_txn.info.success { + return Err(CliError::SimulationError( + simulated_txn.info.vm_status.clone(), + )); + } + + // Take the gas used and use a headroom factor on it + let gas_used = simulated_txn.info.gas_used.0; + let adjusted_max_gas = + adjust_gas_headroom(gas_used, simulated_txn.request.max_gas_amount.0); + + // Ask if you want to accept the estimate amount + let upper_cost_bound = adjusted_max_gas * gas_unit_price; + let lower_cost_bound = gas_used * gas_unit_price; + let message = format!( + "Do you want to submit a transaction for a range of [{} - {}] Octas at a gas unit price of {} Octas?", + lower_cost_bound, + upper_cost_bound, + gas_unit_price); + prompt_yes_with_override(&message, self.prompt_options)?; + adjusted_max_gas + }; + + // Sign and submit transaction + let transaction_factory = TransactionFactory::new(chain_id) + .with_gas_unit_price(gas_unit_price) + .with_max_gas_amount(max_gas) + .with_transaction_expiration_time(self.gas_options.expiration_secs); + let sender_account = &mut LocalAccount::new(sender_address, sender_key, sequence_number); + let transaction = + sender_account.sign_with_transaction_builder(transaction_factory.payload(payload)); + let response = client + .submit_and_wait(&transaction) + .await + .map_err(|err| CliError::ApiError(err.to_string()))?; + + Ok(response.into_inner()) + } + + /// Simulate the transaction locally using the debugger, with the gas profiler enabled. + pub async fn profile_gas( + &self, + payload: TransactionPayload, + ) -> CliTypedResult { + println!(); + println!("Simulating transaction locally with the gas profiler..."); + println!("This is still experimental so results may be inaccurate."); + + let client = self.rest_client()?; + + // Fetch the chain states required for the simulation + // TODO(Gas): get the following from the chain + const DEFAULT_GAS_UNIT_PRICE: u64 = 100; + const DEFAULT_MAX_GAS: u64 = 2_000_000; + + let (sender_key, sender_address) = self.get_key_and_address()?; + let gas_unit_price = self + .gas_options + .gas_unit_price + .unwrap_or(DEFAULT_GAS_UNIT_PRICE); + let (account, state) = get_account_with_state(&client, sender_address).await?; + let version = state.version; + let chain_id = ChainId::new(state.chain_id); + let sequence_number = account.sequence_number; + + let balance = client + .get_account_balance_at_version(sender_address, version) + .await + .map_err(|err| CliError::ApiError(err.to_string()))? + .into_inner(); + + let max_gas = self.gas_options.max_gas.unwrap_or_else(|| { + if gas_unit_price == 0 { + DEFAULT_MAX_GAS + } else { + std::cmp::min(balance.coin.value.0 / gas_unit_price, DEFAULT_MAX_GAS) + } + }); + + // Create and sign the transaction + let transaction_factory = TransactionFactory::new(chain_id) + .with_gas_unit_price(gas_unit_price) + .with_max_gas_amount(max_gas) + .with_transaction_expiration_time(self.gas_options.expiration_secs); + let sender_account = &mut LocalAccount::new(sender_address, sender_key, sequence_number); + let transaction = + sender_account.sign_with_transaction_builder(transaction_factory.payload(payload)); + let hash = transaction.clone().committed_hash(); + + // Execute the transaction using the debugger + let debugger = AptosDebugger::rest_client(client).unwrap(); + let res = debugger.execute_transaction_at_version_with_gas_profiler(version, transaction); + let (vm_status, output, gas_log) = res.map_err(|err| { + CliError::UnexpectedError(format!("failed to simulate txn with gas profiler: {}", err)) + })?; + + // Generate the file name for the flamegraphs + let entry_point = gas_log.entry_point(); + + let human_readable_name = match entry_point { + FrameName::Script => "script".to_string(), + FrameName::Function { + module_id, name, .. + } => { + let addr_short = module_id.address().short_str_lossless(); + let addr_truncated = if addr_short.len() > 4 { + &addr_short[..4] + } else { + addr_short.as_str() + }; + format!("0x{}-{}-{}", addr_truncated, module_id.name(), name) + } + }; + let raw_file_name = format!("txn-{}-{}", hash, human_readable_name); + + // Create the directory if it does not exist yet. + let dir: &Path = Path::new("gas-profiling"); + + macro_rules! create_dir { + () => { + if let Err(err) = std::fs::create_dir(dir) { + if err.kind() != std::io::ErrorKind::AlreadyExists { + return Err(CliError::UnexpectedError(format!( + "failed to create directory {}", + dir.display() + ))); + } + } + }; + } + + // Generate the execution & IO flamegraph. + println!(); + match gas_log.to_flamegraph(format!("Transaction {} -- Execution & IO", hash))? { + Some(graph_bytes) => { + create_dir!(); + let graph_file_path = Path::join(dir, format!("{}.exec_io.svg", raw_file_name)); + std::fs::write(&graph_file_path, graph_bytes).map_err(|err| { + CliError::UnexpectedError(format!( + "Failed to write flamegraph to file {} : {:?}", + graph_file_path.display(), + err + )) + })?; + println!( + "Execution & IO Gas flamegraph saved to {}", + graph_file_path.display() + ); + } + None => { + println!("Skipped generating execution & IO flamegraph"); + } + } + + // Generate the storage fee flamegraph. + match gas_log + .storage + .to_flamegraph(format!("Transaction {} -- Storage Fee", hash))? + { + Some(graph_bytes) => { + create_dir!(); + let graph_file_path = Path::join(dir, format!("{}.storage.svg", raw_file_name)); + std::fs::write(&graph_file_path, graph_bytes).map_err(|err| { + CliError::UnexpectedError(format!( + "Failed to write flamegraph to file {} : {:?}", + graph_file_path.display(), + err + )) + })?; + println!( + "Storage fee flamegraph saved to {}", + graph_file_path.display() + ); + } + None => { + println!("Skipped generating storage fee flamegraph"); + } + } + + println!(); + + // Generate the transaction summary + + // TODO(Gas): double check if this is correct. + let success = match output.status() { + TransactionStatus::Keep(exec_status) => Some(exec_status.is_success()), + TransactionStatus::Discard(_) | TransactionStatus::Retry => None, + }; + + Ok(TransactionSummary { + transaction_hash: hash.into(), + gas_used: Some(output.gas_used()), + gas_unit_price: Some(gas_unit_price), + pending: None, + sender: Some(sender_address), + sequence_number: None, // The transaction is not comitted so there is no new sequence number. + success, + timestamp_us: None, + version: Some(version), // The transaction is not comitted so there is no new version. + vm_status: Some(vm_status.to_string()), + }) + } + + pub async fn estimate_gas_price(&self) -> CliTypedResult { + let client = self.rest_client()?; + client + .estimate_gas_price() + .await + .map(|inner| inner.into_inner().gas_estimate) + .map_err(|err| { + CliError::UnexpectedError(format!( + "Failed to retrieve gas price estimate {:?}", + err + )) + }) + } +} + +#[derive(Parser)] +pub struct OptionalPoolAddressArgs { + /// Address of the Staking pool + /// + /// Defaults to the profile's `AccountAddress` + #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] + pub(crate) pool_address: Option, +} + +#[derive(Parser)] +pub struct PoolAddressArgs { + /// Address of the Staking pool + #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] + pub(crate) pool_address: AccountAddress, +} + +// This struct includes TypeInfo (account_address, module_name, and struct_name) +// and RotationProofChallenge-specific information (sequence_number, originator, current_auth_key, and new_public_key) +// Since the struct RotationProofChallenge is defined in "0x1::account::RotationProofChallenge", +// we will be passing in "0x1" to `account_address`, "account" to `module_name`, and "RotationProofChallenge" to `struct_name` +// Originator refers to the user's address +#[derive(Serialize, Deserialize)] +pub struct RotationProofChallenge { + // Should be `CORE_CODE_ADDRESS` + pub account_address: AccountAddress, + // Should be `account` + pub module_name: String, + // Should be `RotationProofChallenge` + pub struct_name: String, + pub sequence_number: u64, + pub originator: AccountAddress, + pub current_auth_key: AccountAddress, + pub new_public_key: Vec, +} + +#[derive(Debug, Parser)] +/// This is used for both entry functions and scripts. +pub struct ArgWithTypeVec { + /// Arguments combined with their type separated by spaces. + /// + /// Supported types [address, bool, hex, string, u8, u16, u32, u64, u128, u256, raw] + /// + /// Vectors may be specified using JSON array literal syntax (you may need to escape this with + /// quotes based on your shell interpreter) + /// + /// Example: `address:0x1 bool:true u8:0 u256:1234 "bool:[true, false]" 'address:[["0xace", "0xbee"], []]'` + /// + /// Vector is wrapped in a reusable struct for uniform CLI documentation. + #[clap(long, multiple_values = true)] + pub(crate) args: Vec, +} + +/// Common options for constructing an entry function transaction payload. +#[derive(Debug, Parser)] +pub struct EntryFunctionArguments { + /// Function name as `
::::` + /// + /// Example: `0x842ed41fad9640a2ad08fdd7d3e4f7f505319aac7d67e1c0dd6a7cce8732c7e3::message::set_message` + #[clap(long)] + pub function_id: MemberId, + + #[clap(flatten)] + pub(crate) arg_vec: ArgWithTypeVec, + + /// TypeTag arguments separated by spaces. + /// + /// Example: `u8 u16 u32 u64 u128 u256 bool address vector signer` + #[clap(long, multiple_values = true)] + pub type_args: Vec, +} + +impl EntryFunctionArguments { + /// Construct and return an entry function payload from function_id, args, and type_args. + pub fn create_entry_function_payload(self) -> CliTypedResult { + let args: Vec> = self + .arg_vec + .args + .into_iter() + .map(|arg_with_type| arg_with_type.arg) + .collect(); + + let mut parsed_type_args: Vec = Vec::new(); + // These TypeArgs are used for generics + for type_arg in self.type_args.into_iter() { + let type_tag = TypeTag::try_from(type_arg.clone()) + .map_err(|err| CliError::UnableToParse("--type-args", err.to_string()))?; + parsed_type_args.push(type_tag) + } + + Ok(EntryFunction::new( + self.function_id.module_id, + self.function_id.member_id, + parsed_type_args, + args, + )) + } +} + +/// Common options for interactions with a multisig account. +#[derive(Clone, Debug, Parser, Serialize)] +pub struct MultisigAccount { + /// The address of the multisig account to interact with. + #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] + pub(crate) multisig_address: AccountAddress, +} diff --git a/movement-sdk/clis/aptos/src/common/utils.rs b/movement-sdk/clis/aptos/src/common/utils.rs new file mode 100644 index 000000000..126afde04 --- /dev/null +++ b/movement-sdk/clis/aptos/src/common/utils.rs @@ -0,0 +1,507 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::types::{ + account_address_from_public_key, CliError, CliTypedResult, PromptOptions, + TransactionOptions, TransactionSummary, + }, + config::GlobalConfig, + CliResult, +}; +use aptos_build_info::build_information; +use aptos_crypto::ed25519::{Ed25519PrivateKey, Ed25519PublicKey}; +use aptos_keygen::KeyGen; +use aptos_logger::{debug, Level}; +use aptos_rest_client::{aptos_api_types::HashValue, Account, Client, State}; +use aptos_telemetry::service::telemetry_is_disabled; +use aptos_types::{ + account_address::create_multisig_account_address, + chain_id::ChainId, + transaction::{authenticator::AuthenticationKey, TransactionPayload}, +}; +use itertools::Itertools; +use move_core_types::account_address::AccountAddress; +use reqwest::Url; +use serde::Serialize; +#[cfg(unix)] +use std::os::unix::fs::OpenOptionsExt; +use std::{ + collections::BTreeMap, + env, + fs::OpenOptions, + io::Write, + path::{Path, PathBuf}, + str::FromStr, + time::{Duration, Instant, SystemTime}, +}; + +/// Prompts for confirmation until a yes or no is given explicitly +pub fn prompt_yes(prompt: &str) -> bool { + let mut result: Result = Err(()); + + // Read input until a yes or a no is given + while result.is_err() { + println!("{} [yes/no] >", prompt); + let mut input = String::new(); + if std::io::stdin().read_line(&mut input).is_err() { + continue; + } + result = match input.trim().to_lowercase().as_str() { + "yes" | "y" => Ok(true), + "no" | "n" => Ok(false), + _ => Err(()), + }; + } + result.unwrap() +} + +/// Convert any successful response to Success +pub async fn to_common_success_result( + command: &str, + start_time: Instant, + result: CliTypedResult, +) -> CliResult { + to_common_result(command, start_time, result.map(|_| "Success")).await +} + +/// For pretty printing outputs in JSON +pub async fn to_common_result( + command: &str, + start_time: Instant, + result: CliTypedResult, +) -> CliResult { + let latency = start_time.elapsed(); + let is_err = result.is_err(); + + if !telemetry_is_disabled() { + let error = if let Err(ref error) = result { + // Only print the error type + Some(error.to_str()) + } else { + None + }; + send_telemetry_event(command, latency, !is_err, error).await; + } + + let result: ResultWrapper = result.into(); + let string = serde_json::to_string_pretty(&result).unwrap(); + if is_err { + Err(string) + } else { + Ok(string) + } +} + +pub fn cli_build_information() -> BTreeMap { + build_information!() +} + +/// Sends a telemetry event about the CLI build, command and result +async fn send_telemetry_event( + command: &str, + latency: Duration, + success: bool, + error: Option<&str>, +) { + // Collect the build information + let build_information = cli_build_information(); + + // Send the event + aptos_telemetry::cli_metrics::send_cli_telemetry_event( + build_information, + command.into(), + latency, + success, + error, + ) + .await; +} + +/// A result wrapper for displaying either a correct execution result or an error. +/// +/// The purpose of this is to have a pretty easy to recognize JSON output format e.g. +/// +/// { +/// "Result":{ +/// "encoded":{ ... } +/// } +/// } +/// +/// { +/// "Error":"Failed to run command" +/// } +/// +#[derive(Debug, Serialize)] +enum ResultWrapper { + Result(T), + Error(String), +} + +impl From> for ResultWrapper { + fn from(result: CliTypedResult) -> Self { + match result { + Ok(inner) => ResultWrapper::Result(inner), + Err(inner) => ResultWrapper::Error(inner.to_string()), + } + } +} + +/// Checks if a file exists, being overridden by `PromptOptions` +pub fn check_if_file_exists(file: &Path, prompt_options: PromptOptions) -> CliTypedResult<()> { + if file.exists() { + prompt_yes_with_override( + &format!( + "{:?} already exists, are you sure you want to overwrite it?", + file.as_os_str(), + ), + prompt_options, + )? + } + + Ok(()) +} + +pub fn prompt_yes_with_override(prompt: &str, prompt_options: PromptOptions) -> CliTypedResult<()> { + if prompt_options.assume_no { + return Err(CliError::AbortedError); + } else if prompt_options.assume_yes { + return Ok(()); + } + + let is_yes = if let Some(response) = GlobalConfig::load()?.get_default_prompt_response() { + response + } else { + prompt_yes(prompt) + }; + + if is_yes { + Ok(()) + } else { + Err(CliError::AbortedError) + } +} + +pub fn read_from_file(path: &Path) -> CliTypedResult> { + std::fs::read(path) + .map_err(|e| CliError::UnableToReadFile(format!("{}", path.display()), e.to_string())) +} + +/// Write a `&[u8]` to a file +pub fn write_to_file(path: &Path, name: &str, bytes: &[u8]) -> CliTypedResult<()> { + write_to_file_with_opts(path, name, bytes, &mut OpenOptions::new()) +} + +/// Write a User only read / write file +pub fn write_to_user_only_file(path: &Path, name: &str, bytes: &[u8]) -> CliTypedResult<()> { + let mut opts = OpenOptions::new(); + #[cfg(unix)] + opts.mode(0o600); + write_to_file_with_opts(path, name, bytes, &mut opts) +} + +/// Write a `&[u8]` to a file with the given options +pub fn write_to_file_with_opts( + path: &Path, + name: &str, + bytes: &[u8], + opts: &mut OpenOptions, +) -> CliTypedResult<()> { + let mut file = opts + .write(true) + .create(true) + .truncate(true) + .open(path) + .map_err(|e| CliError::IO(name.to_string(), e))?; + file.write_all(bytes) + .map_err(|e| CliError::IO(name.to_string(), e)) +} + +/// Appends a file extension to a `Path` without overwriting the original extension. +pub fn append_file_extension( + file: &Path, + appended_extension: &'static str, +) -> CliTypedResult { + let extension = file + .extension() + .map(|extension| extension.to_str().unwrap_or_default()); + if let Some(extension) = extension { + Ok(file.with_extension(extension.to_owned() + "." + appended_extension)) + } else { + Ok(file.with_extension(appended_extension)) + } +} + +/// Retrieves account resource from the rest client +pub async fn get_account( + client: &aptos_rest_client::Client, + address: AccountAddress, +) -> CliTypedResult { + let account_response = client + .get_account(address) + .await + .map_err(|err| CliError::ApiError(err.to_string()))?; + Ok(account_response.into_inner()) +} + +/// Retrieves account resource from the rest client +pub async fn get_account_with_state( + client: &aptos_rest_client::Client, + address: AccountAddress, +) -> CliTypedResult<(Account, State)> { + let account_response = client + .get_account(address) + .await + .map_err(|err| CliError::ApiError(err.to_string()))?; + Ok(account_response.into_parts()) +} + +/// Retrieves sequence number from the rest client +pub async fn get_sequence_number( + client: &aptos_rest_client::Client, + address: AccountAddress, +) -> CliTypedResult { + Ok(get_account(client, address).await?.sequence_number) +} + +/// Retrieves the auth key from the rest client +pub async fn get_auth_key( + client: &aptos_rest_client::Client, + address: AccountAddress, +) -> CliTypedResult { + Ok(get_account(client, address).await?.authentication_key) +} + +/// Retrieves the chain id from the rest client +pub async fn chain_id(rest_client: &Client) -> CliTypedResult { + let state = rest_client + .get_ledger_information() + .await + .map_err(|err| CliError::ApiError(err.to_string()))? + .into_inner(); + Ok(ChainId::new(state.chain_id)) +} + +/// Error message for parsing a map +const PARSE_MAP_SYNTAX_MSG: &str = "Invalid syntax for map. Example: Name=Value,Name2=Value"; + +/// Parses an inline map of values +/// +/// Example: Name=Value,Name2=Value +pub fn parse_map(str: &str) -> anyhow::Result> +where + K::Err: 'static + std::error::Error + Send + Sync, + V::Err: 'static + std::error::Error + Send + Sync, +{ + let mut map = BTreeMap::new(); + + // Split pairs by commas + for pair in str.split_terminator(',') { + // Split pairs by = then trim off any spacing + let (first, second): (&str, &str) = pair + .split_terminator('=') + .collect_tuple() + .ok_or_else(|| anyhow::Error::msg(PARSE_MAP_SYNTAX_MSG))?; + let first = first.trim(); + let second = second.trim(); + if first.is_empty() || second.is_empty() { + return Err(anyhow::Error::msg(PARSE_MAP_SYNTAX_MSG)); + } + + // At this point, we just give error messages appropriate to parsing + let key: K = K::from_str(first)?; + let value: V = V::from_str(second)?; + map.insert(key, value); + } + Ok(map) +} + +/// Generate a vanity account for Ed25519 single signer scheme, either standard or multisig. +/// +/// The default authentication key for an Ed25519 account is the same as the account address. Hence +/// for a standard account, this function generates Ed25519 private keys until finding one that has +/// an authentication key (account address) that begins with the given vanity prefix. +/// +/// For a multisig account, this function generates private keys until finding one that can create +/// a multisig account with the given vanity prefix as its first transaction (sequence number 0). +/// +/// Note that while a valid hex string must have an even number of characters, a vanity prefix can +/// have an odd number of characters since account addresses are human-readable. +/// +/// `vanity_prefix_ref` is a reference to a hex string vanity prefix, optionally prefixed with "0x". +/// For example "0xaceface" or "d00d". +pub fn generate_vanity_account_ed25519( + vanity_prefix_ref: &str, + multisig: bool, +) -> CliTypedResult { + let vanity_prefix_ref = vanity_prefix_ref + .strip_prefix("0x") + .unwrap_or(vanity_prefix_ref); // Optionally strip leading 0x from input string. + let mut to_check_if_is_hex = String::from(vanity_prefix_ref); + // If an odd number of characters append a 0 for verifying that prefix contains valid hex. + if to_check_if_is_hex.len() % 2 != 0 { + to_check_if_is_hex += "0" + }; + hex::decode(to_check_if_is_hex). // Check that the vanity prefix can be decoded into hex. + map_err(|error| CliError::CommandArgumentError(format!( + "The vanity prefix could not be decoded to hex: {}", error)))?; + let mut key_generator = KeyGen::from_os_rng(); // Get random key generator. + loop { + // Generate new keys until finding a match against the vanity prefix. + let private_key = key_generator.generate_ed25519_private_key(); + let mut account_address = + account_address_from_public_key(&Ed25519PublicKey::from(&private_key)); + if multisig { + account_address = create_multisig_account_address(account_address, 0) + }; + if account_address + .short_str_lossless() + .starts_with(vanity_prefix_ref) + { + return Ok(private_key); + }; + } +} + +pub fn current_dir() -> CliTypedResult { + env::current_dir().map_err(|err| { + CliError::UnexpectedError(format!("Failed to get current directory {}", err)) + }) +} + +pub fn dir_default_to_current(maybe_dir: Option) -> CliTypedResult { + if let Some(dir) = maybe_dir { + Ok(dir) + } else { + current_dir() + } +} + +pub fn create_dir_if_not_exist(dir: &Path) -> CliTypedResult<()> { + // Check if the directory exists, if it's not a dir, it will also fail here + if !dir.exists() || !dir.is_dir() { + std::fs::create_dir_all(dir).map_err(|e| CliError::IO(dir.display().to_string(), e))?; + debug!("Created {} folder", dir.display()); + } else { + debug!("{} folder already exists", dir.display()); + } + Ok(()) +} + +/// Reads a line from input +pub fn read_line(input_name: &'static str) -> CliTypedResult { + let mut input_buf = String::new(); + let _ = std::io::stdin() + .read_line(&mut input_buf) + .map_err(|err| CliError::IO(input_name.to_string(), err))?; + + Ok(input_buf) +} + +/// Fund account (and possibly create it) from a faucet +pub async fn fund_account( + faucet_url: Url, + num_octas: u64, + address: AccountAddress, +) -> CliTypedResult> { + let response = reqwest::Client::new() + .post(format!( + "{}v1/mint?amount={}&auth_key={}", + faucet_url, num_octas, address + )) + .body("{}") + .send() + .await + .map_err(|err| { + CliError::ApiError(format!("Failed to fund account with faucet: {:#}", err)) + })?; + if response.status() == 200 { + let hashes: Vec = response + .json() + .await + .map_err(|err| CliError::UnexpectedError(err.to_string()))?; + Ok(hashes) + } else { + Err(CliError::ApiError(format!( + "Faucet issue: {}", + response.status() + ))) + } +} + +/// Fund by public key (and possibly create it) from a faucet +pub async fn fund_pub_key( + faucet_url: Url, + pub_key: String, +) -> CliTypedResult> { + let url = format!( + "{}v1/mint?&pub_key={}", + faucet_url, pub_key + ); + let response = reqwest::Client::new() + .post(url) + .body("{}") + .send() + .await + .map_err(|err| { + CliError::ApiError(format!("Failed to fund account with faucet: {:#}", err)) + })?; + if response.status() == 200 { + let hashes: Vec = response + .json() + .await + .map_err(|err| CliError::UnexpectedError(err.to_string()))?; + Ok(hashes) + } else { + Err(CliError::ApiError(format!( + "Faucet issue: {}", + response.status() + ))) + } +} + +/// Wait for transactions, returning an error if any of them fail. +pub async fn wait_for_transactions( + client: &aptos_rest_client::Client, + hashes: Vec, +) -> CliTypedResult<()> { + let sys_time = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .map_err(|e| CliError::UnexpectedError(e.to_string()))? + .as_secs() + + 30; + for hash in hashes { + client + .wait_for_transaction_by_hash( + hash.into(), + sys_time, + Some(Duration::from_secs(60)), + None, + ) + .await?; + } + Ok(()) +} + +pub fn start_logger() { + let mut logger = aptos_logger::Logger::new(); + logger.channel_size(1000).is_async(false).level(Level::Warn); + logger.build(); +} + +/// For transaction payload and options, either get gas profile or submit for execution. +pub async fn profile_or_submit( + payload: TransactionPayload, + txn_options_ref: &TransactionOptions, +) -> CliTypedResult { + // Profile gas if needed. + if txn_options_ref.profile_gas { + txn_options_ref.profile_gas(payload).await + } else { + // Otherwise submit the transaction. + txn_options_ref + .submit_transaction(payload) + .await + .map(TransactionSummary::from) + } +} diff --git a/movement-sdk/clis/aptos/src/config/mod.rs b/movement-sdk/clis/aptos/src/config/mod.rs new file mode 100644 index 000000000..af4da540f --- /dev/null +++ b/movement-sdk/clis/aptos/src/config/mod.rs @@ -0,0 +1,360 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{ + types::{ + CliCommand, CliConfig, CliError, CliResult, CliTypedResult, ConfigSearchMode, + ProfileSummary, CONFIG_FOLDER, + }, + utils::{create_dir_if_not_exist, current_dir, read_from_file, write_to_user_only_file}, + }, + genesis::git::{from_yaml, to_yaml}, + Tool, +}; +use async_trait::async_trait; +use clap::{ArgEnum, CommandFactory, Parser}; +use clap_complete::{generate, Shell}; +use serde::{Deserialize, Serialize}; +use std::{collections::BTreeMap, fmt::Formatter, path::PathBuf, str::FromStr}; + +/// Tool for interacting with configuration of the Movement CLI tool +/// +/// This tool handles the global configuration of the CLI tool for +/// default configuration, and user specific settings. +#[derive(Parser)] +pub enum ConfigTool { + Init(crate::common::init::InitTool), + GenerateShellCompletions(GenerateShellCompletions), + SetGlobalConfig(SetGlobalConfig), + ShowGlobalConfig(ShowGlobalConfig), + ShowProfiles(ShowProfiles), +} + +impl ConfigTool { + pub async fn execute(self) -> CliResult { + match self { + ConfigTool::Init(tool) => tool.execute_serialized_success().await, + ConfigTool::GenerateShellCompletions(tool) => tool.execute_serialized_success().await, + ConfigTool::SetGlobalConfig(tool) => tool.execute_serialized().await, + ConfigTool::ShowGlobalConfig(tool) => tool.execute_serialized().await, + ConfigTool::ShowProfiles(tool) => tool.execute_serialized().await, + } + } +} + +/// Generate shell completion files +/// +/// First generate the completion file, then follow the shell specific directions on how +/// to install the completion file. +#[derive(Parser)] +pub struct GenerateShellCompletions { + /// Shell to generate completions for one of [bash, elvish, powershell, zsh] + #[clap(long)] + shell: Shell, + + /// File to output shell completions to + #[clap(long, parse(from_os_str))] + output_file: PathBuf, +} + +#[async_trait] +impl CliCommand<()> for GenerateShellCompletions { + fn command_name(&self) -> &'static str { + "GenerateShellCompletions" + } + + async fn execute(self) -> CliTypedResult<()> { + let mut command = Tool::command(); + let mut file = std::fs::File::create(self.output_file.as_path()) + .map_err(|err| CliError::IO(self.output_file.display().to_string(), err))?; + generate(self.shell, &mut command, "movement".to_string(), &mut file); + Ok(()) + } +} + +/// Set global configuration settings +/// +/// Any configuration flags that are not provided will not be changed +#[derive(Parser, Debug)] +pub struct SetGlobalConfig { + /// A configuration for where to place and use the config + /// + /// `Workspace` will put the `.aptos/` folder in the current directory, where + /// `Global` will put the `.aptos/` folder in your home directory + #[clap(long)] + config_type: Option, + /// A configuration for how to expect the prompt response + /// + /// Option can be one of ["yes", "no", "prompt"], "yes" runs cli with "--assume-yes", where + /// "no" runs cli with "--assume-no", default: "prompt" + #[clap(long)] + default_prompt_response: Option, +} + +#[async_trait] +impl CliCommand for SetGlobalConfig { + fn command_name(&self) -> &'static str { + "SetGlobalConfig" + } + + async fn execute(self) -> CliTypedResult { + // Load the global config + let mut config = GlobalConfig::load()?; + + // Enable all features that are actually listed + if let Some(config_type) = self.config_type { + config.config_type = Some(config_type); + } + + if let Some(default_prompt_response) = self.default_prompt_response { + config.default_prompt_response = default_prompt_response; + } + + config.save()?; + config.display() + } +} + +/// Shows the current profiles available +/// +/// This will only show public information and will not show +/// private information +#[derive(Parser, Debug)] +pub struct ShowProfiles { + /// Which profile to show + /// + /// If provided, show only this profile + #[clap(long)] + profile: Option, +} + +#[async_trait] +impl CliCommand> for ShowProfiles { + fn command_name(&self) -> &'static str { + "ShowProfiles" + } + + async fn execute(self) -> CliTypedResult> { + // Load the profile config + let config = CliConfig::load(ConfigSearchMode::CurrentDir)?; + Ok(config + .profiles + .unwrap_or_default() + .into_iter() + .filter(|(key, _)| { + if let Some(ref profile) = self.profile { + profile == key + } else { + true + } + }) + .map(|(key, profile)| (key, ProfileSummary::from(&profile))) + .collect()) + } +} + +/// Shows the properties in the global config +#[derive(Parser, Debug)] +pub struct ShowGlobalConfig {} + +#[async_trait] +impl CliCommand for ShowGlobalConfig { + fn command_name(&self) -> &'static str { + "ShowGlobalConfig" + } + + async fn execute(self) -> CliTypedResult { + // Load the global config + let config = GlobalConfig::load()?; + + config.display() + } +} + +const GLOBAL_CONFIG_FILE: &str = "global_config.yaml"; + +/// A global configuration for global settings related to a user +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct GlobalConfig { + /// Whether to be using Global or Workspace mode + #[serde(skip_serializing_if = "Option::is_none")] + pub config_type: Option, + /// Prompt response type + #[serde(default)] + pub default_prompt_response: PromptResponseType, +} + +impl GlobalConfig { + /// Fill in defaults for display via the CLI + pub fn display(mut self) -> CliTypedResult { + if self.config_type.is_none() { + self.config_type = Some(ConfigType::default()); + } + + Ok(self) + } + + pub fn load() -> CliTypedResult { + let path = global_folder()?.join(GLOBAL_CONFIG_FILE); + if path.exists() { + from_yaml(&String::from_utf8(read_from_file(path.as_path())?)?) + } else { + // If we don't have a config, let's load the default + Ok(GlobalConfig::default()) + } + } + + /// Get the config location based on the type + pub fn get_config_location(&self, mode: ConfigSearchMode) -> CliTypedResult { + match self.config_type.unwrap_or_default() { + ConfigType::Global => global_folder(), + ConfigType::Workspace => find_workspace_config(current_dir()?, mode), + } + } + + /// Get the prompt options from global config + pub fn get_default_prompt_response(&self) -> Option { + match self.default_prompt_response { + PromptResponseType::Prompt => None, // prompt + PromptResponseType::Yes => Some(true), // assume_yes + PromptResponseType::No => Some(false), // assume_no + } + } + + fn save(&self) -> CliTypedResult<()> { + let global_folder = global_folder()?; + create_dir_if_not_exist(global_folder.as_path())?; + + write_to_user_only_file( + global_folder.join(GLOBAL_CONFIG_FILE).as_path(), + "Global Config", + &to_yaml(&self)?.into_bytes(), + ) + } +} + +fn global_folder() -> CliTypedResult { + if let Some(dir) = dirs::home_dir() { + Ok(dir.join(CONFIG_FOLDER)) + } else { + Err(CliError::UnexpectedError( + "Unable to retrieve home directory".to_string(), + )) + } +} + +fn find_workspace_config( + starting_path: PathBuf, + mode: ConfigSearchMode, +) -> CliTypedResult { + match mode { + ConfigSearchMode::CurrentDir => Ok(starting_path.join(CONFIG_FOLDER)), + ConfigSearchMode::CurrentDirAndParents => { + let mut current_path = starting_path.clone(); + loop { + current_path.push(CONFIG_FOLDER); + if current_path.is_dir() { + break Ok(current_path); + } else if !(current_path.pop() && current_path.pop()) { + // If we aren't able to find the folder, we'll create a new one right here + break Ok(starting_path.join(CONFIG_FOLDER)); + } + } + }, + } +} + +const GLOBAL: &str = "global"; +const WORKSPACE: &str = "workspace"; + +/// A configuration for where to place and use the config +/// +/// Workspace allows for multiple configs based on location, where +/// Global allows for one config for every part of the code +#[derive(Debug, Copy, Clone, Serialize, Deserialize, ArgEnum)] +pub enum ConfigType { + /// Per system user configuration put in `/.aptos` + Global, + /// Per directory configuration put in `/.aptos` + Workspace, +} + +impl Default for ConfigType { + fn default() -> Self { + // TODO: When we version up, we can change this to global + Self::Workspace + } +} + +impl std::fmt::Display for ConfigType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + ConfigType::Global => GLOBAL, + ConfigType::Workspace => WORKSPACE, + }) + } +} + +impl FromStr for ConfigType { + type Err = CliError; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().trim() { + GLOBAL => Ok(Self::Global), + WORKSPACE => Ok(Self::Workspace), + _ => Err(CliError::CommandArgumentError( + "Invalid config type, must be one of [global, workspace]".to_string(), + )), + } + } +} + +const PROMPT: &str = "prompt"; +const ASSUME_YES: &str = "yes"; +const ASSUME_NO: &str = "no"; + +/// A configuration for how to expect the prompt response +/// +/// Option can be one of ["yes", "no", "prompt"], "yes" runs cli with "--assume-yes", where +/// "no" runs cli with "--assume-no", default: "prompt" +#[derive(Debug, Copy, Clone, Serialize, Deserialize, ArgEnum)] +pub enum PromptResponseType { + /// normal prompt + Prompt, + /// `--assume-yes` + Yes, + /// `--assume-no` + No, +} + +impl Default for PromptResponseType { + fn default() -> Self { + Self::Prompt + } +} + +impl std::fmt::Display for PromptResponseType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + PromptResponseType::Prompt => PROMPT, + PromptResponseType::Yes => ASSUME_YES, + PromptResponseType::No => ASSUME_NO, + }) + } +} + +impl FromStr for PromptResponseType { + type Err = CliError; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().trim() { + PROMPT => Ok(Self::Prompt), + ASSUME_YES => Ok(Self::Yes), + ASSUME_NO => Ok(Self::No), + _ => Err(CliError::CommandArgumentError( + "Invalid prompt response type, must be one of [yes, no, prompt]".to_string(), + )), + } + } +} diff --git a/movement-sdk/clis/aptos/src/faucet/mod.rs b/movement-sdk/clis/aptos/src/faucet/mod.rs new file mode 100644 index 000000000..4b4a6d91b --- /dev/null +++ b/movement-sdk/clis/aptos/src/faucet/mod.rs @@ -0,0 +1,52 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::{ + types::{CliCommand, CliTypedResult} +}; +use async_trait::async_trait; +use crate::common::types::{FaucetOptions, ProfileOptions, RestOptions}; +use crate::common::utils::{fund_pub_key, wait_for_transactions}; +use clap::Parser; + +#[derive(Debug, Parser)] +pub struct FaucetTool { + #[clap(long)] + pub_key: Option, + #[clap(flatten)] + pub(crate) faucet_options: FaucetOptions, + #[clap(flatten)] + pub(crate) rest_options: RestOptions, + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, +} + +impl FaucetTool { + fn pub_key(&self, profile: &ProfileOptions) -> CliTypedResult { + match &self.pub_key { + Some(pub_key) => Ok(pub_key.clone()), + None => Ok((profile.public_key()?).to_string()), + } + } +} + +#[async_trait] +impl CliCommand for FaucetTool { + fn command_name(&self) -> &'static str { + "Faucet" + } + + async fn execute(self) -> CliTypedResult { + let profile = &self.profile_options; + let hashes = fund_pub_key( + self.faucet_options.faucet_url(&profile)?, + self.pub_key(&profile)?, + ).await?; + let client = self.rest_options.client_raw(self.faucet_options.faucet_url(&profile)?)?; + wait_for_transactions(&client, hashes).await?; + return Ok(format!( + "Added 10 MOV to account {}", self.pub_key(&profile)? + )); + } +} + diff --git a/movement-sdk/clis/aptos/src/ffi.rs b/movement-sdk/clis/aptos/src/ffi.rs new file mode 100644 index 000000000..af9ba937a --- /dev/null +++ b/movement-sdk/clis/aptos/src/ffi.rs @@ -0,0 +1,85 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#![allow(unsafe_code)] + +use crate::Tool; +use clap::Parser; +use std::{ + ffi::{c_char, CStr, CString}, + thread, +}; +use tokio::runtime::Runtime; + +/// # Safety +/// +/// Run the movement CLI synchronously +/// Note: This function should only be called from other SDK (i.g Typescript) +/// +/// Return: the pointer to CLIResult c string +#[no_mangle] +pub unsafe extern "C" fn run_aptos_sync(s: *const c_char) -> *const c_char { + let c_str = unsafe { + assert!(!s.is_null()); + CStr::from_ptr(s) + }; + + // split string by spaces + let input_string = c_str.to_str().unwrap().split_whitespace(); + + // Create a new Tokio runtime and block on the execution of `cli.execute()` + let result_string = Runtime::new().unwrap().block_on(async move { + let cli = Tool::parse_from(input_string); + let result = cli.execute().await; + result + }); + + let res_cstr = CString::new(result_string.unwrap()).unwrap(); + + // Return a pointer to the C string + res_cstr.into_raw() +} + +/// # Safety +/// +/// Run the movement CLI async; Use this function if you are expecting the movement CLI command +/// to run in the background, or different thread +/// Note: This function should only be called from other SDK (i.g Typescript) +/// +/// Return: the pointer to c string: 'true' +#[no_mangle] +pub unsafe extern "C" fn run_aptos_async(s: *mut c_char) -> *mut c_char { + println!("Running movement..."); + let c_str = unsafe { + assert!(!s.is_null()); + CStr::from_ptr(s) + }; + + // Spawn a new thread to run the CLI + thread::spawn(move || { + let rt = Runtime::new().unwrap(); + let input_string = c_str.to_str().unwrap().split_whitespace(); + let cli = Tool::parse_from(input_string); + + // Run the CLI once + rt.block_on(async { cli.execute().await }) + .expect("Failed to run CLI"); + }); + + // Return pointer + CString::new("true").unwrap().into_raw() +} + +/// # Safety +/// +/// After running the movement CLI using FFI. Make sure to invoke this method to free up or +/// deallocate the memory +#[no_mangle] +pub unsafe extern "C" fn free_cstring(s: *mut c_char) { + unsafe { + if s.is_null() { + return; + } + CString::from_raw(s) + }; +} diff --git a/movement-sdk/clis/aptos/src/genesis/git.rs b/movement-sdk/clis/aptos/src/genesis/git.rs new file mode 100644 index 000000000..383689151 --- /dev/null +++ b/movement-sdk/clis/aptos/src/genesis/git.rs @@ -0,0 +1,264 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{ + types::{CliError, CliTypedResult}, + utils::{create_dir_if_not_exist, write_to_file}, + }, + CliCommand, +}; +use aptos_config::config::Token; +use aptos_framework::ReleaseBundle; +use aptos_genesis::config::Layout; +use aptos_github_client::Client as GithubClient; +use async_trait::async_trait; +use clap::Parser; +use serde::{de::DeserializeOwned, Serialize}; +use std::{ + fmt::Debug, + io::Read, + path::{Path, PathBuf}, + str::FromStr, +}; + +pub const LAYOUT_FILE: &str = "layout.yaml"; +pub const OPERATOR_FILE: &str = "operator.yaml"; +pub const OWNER_FILE: &str = "owner.yaml"; +pub const FRAMEWORK_NAME: &str = "framework.mrb"; +pub const BALANCES_FILE: &str = "balances.yaml"; +pub const EMPLOYEE_VESTING_ACCOUNTS_FILE: &str = "employee_vesting_accounts.yaml"; + +/// Setup a shared Git repository for Genesis +/// +/// This will setup a folder or an online Github repository to be used +/// for Genesis. If it's the local, it will create the folders but not +/// set up a Git repository. +#[derive(Parser)] +pub struct SetupGit { + #[clap(flatten)] + pub(crate) git_options: GitOptions, + + /// Path to the `Layout` file which defines where all the files are + #[clap(long, parse(from_os_str))] + pub(crate) layout_file: PathBuf, +} + +#[async_trait] +impl CliCommand<()> for SetupGit { + fn command_name(&self) -> &'static str { + "SetupGit" + } + + async fn execute(self) -> CliTypedResult<()> { + let layout = Layout::from_disk(&self.layout_file)?; + + // Upload layout file to ensure we can read later + let client = self.git_options.get_client()?; + client.put(Path::new(LAYOUT_FILE), &layout)?; + + Ok(()) + } +} + +#[derive(Clone, Debug, Default)] +pub struct GithubRepo { + owner: String, + repository: String, +} + +impl FromStr for GithubRepo { + type Err = CliError; + + fn from_str(s: &str) -> Result { + let parts: Vec<_> = s.split('/').collect(); + if parts.len() != 2 { + Err(CliError::CommandArgumentError("Invalid repository must be of the form 'owner/repository` e.g. 'aptos-labs/aptos-core'".to_string())) + } else { + Ok(GithubRepo { + owner: parts.first().unwrap().to_string(), + repository: parts.get(1).unwrap().to_string(), + }) + } + } +} + +#[derive(Clone, Default, Parser)] +pub struct GitOptions { + /// Github repository e.g. 'aptos-labs/aptos-core' + /// + /// Mutually exclusive with `--local-repository-dir` + #[clap(long)] + pub(crate) github_repository: Option, + + /// Github repository branch e.g. main + #[clap(long, default_value = "main")] + pub(crate) github_branch: String, + + /// Path to Github API token. Token must have repo:* permissions + #[clap(long, parse(from_os_str))] + pub(crate) github_token_file: Option, + + /// Path to local git repository + /// + /// Mutually exclusive with `--github-repository` + #[clap(long, parse(from_os_str))] + pub(crate) local_repository_dir: Option, +} + +impl GitOptions { + pub fn get_client(self) -> CliTypedResult { + if self.github_repository.is_none() + && self.github_token_file.is_none() + && self.local_repository_dir.is_some() + { + Ok(Client::local(self.local_repository_dir.unwrap())) + } else if self.github_repository.is_some() + && self.github_token_file.is_some() + && self.local_repository_dir.is_none() + { + Client::github( + self.github_repository.unwrap(), + self.github_branch, + self.github_token_file.unwrap(), + ) + } else { + Err(CliError::CommandArgumentError("Must provide either only --local-repository-dir or both --github-repository and --github-token-path".to_string())) + } + } +} + +/// A client for abstracting away local vs Github storage +/// +/// Note: Writes do not commit locally +pub enum Client { + Local(PathBuf), + Github(GithubClient), +} + +impl Client { + pub fn local(path: PathBuf) -> Client { + Client::Local(path) + } + + pub fn github( + repository: GithubRepo, + branch: String, + token_path: PathBuf, + ) -> CliTypedResult { + let token = Token::FromDisk(token_path).read_token()?; + Ok(Client::Github(GithubClient::new( + repository.owner, + repository.repository, + branch, + token, + ))) + } + + /// Retrieves an object as a YAML encoded file from the appropriate storage + pub fn get(&self, path: &Path) -> CliTypedResult { + match self { + Client::Local(local_repository_path) => { + let path = local_repository_path.join(path); + + if !path.exists() { + return Err(CliError::UnableToReadFile( + path.display().to_string(), + "File not found".to_string(), + )); + } + + eprintln!("Reading {}", path.display()); + let mut file = std::fs::File::open(path.as_path()) + .map_err(|e| CliError::IO(path.display().to_string(), e))?; + + let mut contents = String::new(); + file.read_to_string(&mut contents) + .map_err(|e| CliError::IO(path.display().to_string(), e))?; + from_yaml(&contents) + }, + Client::Github(client) => { + from_base64_encoded_yaml(&client.get_file(&path.display().to_string())?) + }, + } + } + + /// Puts an object as a YAML encoded file to the appropriate storage + pub fn put(&self, name: &Path, input: &T) -> CliTypedResult<()> { + match self { + Client::Local(local_repository_path) => { + let path = local_repository_path.join(name); + + // Create repository path and any sub-directories + if let Some(dir) = path.parent() { + self.create_dir(dir)?; + } else { + return Err(CliError::UnexpectedError(format!( + "Path should always have a parent {}", + path.display() + ))); + } + write_to_file( + path.as_path(), + &path.display().to_string(), + to_yaml(input)?.as_bytes(), + )?; + }, + Client::Github(client) => { + client.put(&name.display().to_string(), &to_base64_encoded_yaml(input)?)?; + }, + } + + Ok(()) + } + + pub fn create_dir(&self, dir: &Path) -> CliTypedResult<()> { + match self { + Client::Local(local_repository_path) => { + let path = local_repository_path.join(dir); + create_dir_if_not_exist(path.as_path())?; + }, + Client::Github(_) => { + // There's no such thing as an empty directory in Git, so do nothing + }, + } + + Ok(()) + } + + /// Retrieve framework release bundle. + pub fn get_framework(&self) -> CliTypedResult { + match self { + Client::Local(local_repository_path) => { + let path = local_repository_path.join(FRAMEWORK_NAME); + if !path.exists() { + return Err(CliError::UnableToReadFile( + path.display().to_string(), + "File not found".to_string(), + )); + } + Ok(ReleaseBundle::read(path)?) + }, + Client::Github(client) => { + let bytes = base64::decode(client.get_file(FRAMEWORK_NAME)?)?; + Ok(bcs::from_bytes::(&bytes)?) + }, + } + } +} + +pub fn to_yaml(input: &T) -> CliTypedResult { + Ok(serde_yaml::to_string(input)?) +} + +pub fn from_yaml(input: &str) -> CliTypedResult { + Ok(serde_yaml::from_str(input)?) +} + +pub fn to_base64_encoded_yaml(input: &T) -> CliTypedResult { + Ok(base64::encode(to_yaml(input)?)) +} + +pub fn from_base64_encoded_yaml(input: &str) -> CliTypedResult { + from_yaml(&String::from_utf8(base64::decode(input)?)?) +} diff --git a/movement-sdk/clis/aptos/src/genesis/keys.rs b/movement-sdk/clis/aptos/src/genesis/keys.rs new file mode 100644 index 000000000..c283ad7f4 --- /dev/null +++ b/movement-sdk/clis/aptos/src/genesis/keys.rs @@ -0,0 +1,344 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{ + types::{CliError, CliTypedResult, OptionalPoolAddressArgs, PromptOptions, RngArgs}, + utils::{ + check_if_file_exists, create_dir_if_not_exist, current_dir, dir_default_to_current, + read_from_file, write_to_user_only_file, + }, + }, + genesis::git::{from_yaml, to_yaml, GitOptions, LAYOUT_FILE, OPERATOR_FILE, OWNER_FILE}, + governance::CompileScriptFunction, + CliCommand, +}; +use aptos_genesis::{ + config::{HostAndPort, Layout, OperatorConfiguration, OwnerConfiguration}, + keys::{generate_key_objects, PublicIdentity}, +}; +use aptos_types::{ + account_address::AccountAddress, + transaction::{Script, Transaction, WriteSetPayload}, +}; +use async_trait::async_trait; +use clap::Parser; +use std::path::{Path, PathBuf}; + +const PRIVATE_KEYS_FILE: &str = "private-keys.yaml"; +pub const PUBLIC_KEYS_FILE: &str = "public-keys.yaml"; +const VALIDATOR_FILE: &str = "validator-identity.yaml"; +const VFN_FILE: &str = "validator-full-node-identity.yaml"; + +/// Generate keys for a new validator +/// +/// Generates account key, consensus key, and network key for a validator +/// These keys are used for running a validator or operator in a network +#[derive(Parser)] +pub struct GenerateKeys { + /// Output directory for the key files + #[clap(long, parse(from_os_str))] + pub(crate) output_dir: Option, + + #[clap(flatten)] + pub(crate) pool_address_args: OptionalPoolAddressArgs, + #[clap(flatten)] + pub(crate) prompt_options: PromptOptions, + #[clap(flatten)] + pub rng_args: RngArgs, +} + +#[async_trait] +impl CliCommand> for GenerateKeys { + fn command_name(&self) -> &'static str { + "GenerateKeys" + } + + async fn execute(self) -> CliTypedResult> { + let output_dir = dir_default_to_current(self.output_dir.clone())?; + + let private_keys_file = output_dir.join(PRIVATE_KEYS_FILE); + let public_keys_file = output_dir.join(PUBLIC_KEYS_FILE); + let validator_file = output_dir.join(VALIDATOR_FILE); + let vfn_file = output_dir.join(VFN_FILE); + check_if_file_exists(private_keys_file.as_path(), self.prompt_options)?; + check_if_file_exists(public_keys_file.as_path(), self.prompt_options)?; + check_if_file_exists(validator_file.as_path(), self.prompt_options)?; + check_if_file_exists(vfn_file.as_path(), self.prompt_options)?; + + let mut key_generator = self.rng_args.key_generator()?; + let (mut validator_blob, mut vfn_blob, private_identity, public_identity) = + generate_key_objects(&mut key_generator)?; + + // Allow for the owner to be different than the operator + if let Some(pool_address) = self.pool_address_args.pool_address { + validator_blob.account_address = Some(pool_address); + vfn_blob.account_address = Some(pool_address); + } + + // Create the directory if it doesn't exist + create_dir_if_not_exist(output_dir.as_path())?; + + write_to_user_only_file( + private_keys_file.as_path(), + PRIVATE_KEYS_FILE, + to_yaml(&private_identity)?.as_bytes(), + )?; + write_to_user_only_file( + public_keys_file.as_path(), + PUBLIC_KEYS_FILE, + to_yaml(&public_identity)?.as_bytes(), + )?; + write_to_user_only_file( + validator_file.as_path(), + VALIDATOR_FILE, + to_yaml(&validator_blob)?.as_bytes(), + )?; + write_to_user_only_file(vfn_file.as_path(), VFN_FILE, to_yaml(&vfn_blob)?.as_bytes())?; + Ok(vec![ + public_keys_file, + private_keys_file, + validator_file, + vfn_file, + ]) + } +} + +/// Set validator configuration for a single validator +/// +/// This will set the validator configuration for a single validator in the git repository. +/// It will have to be run for each validator expected at genesis. +#[derive(Parser)] +pub struct SetValidatorConfiguration { + /// Name of the validator + #[clap(long)] + pub(crate) username: String, + + /// Host and port pair for the validator e.g. 127.0.0.1:6180 or aptoslabs.com:6180 + #[clap(long)] + pub(crate) validator_host: HostAndPort, + + /// Host and port pair for the fullnode e.g. 127.0.0.1:6180 or aptoslabs.com:6180 + #[clap(long)] + pub(crate) full_node_host: Option, + + /// Stake amount for stake distribution + #[clap(long, default_value_t = 1)] + pub(crate) stake_amount: u64, + + /// Commission rate to pay operator + /// + /// This is a percentage between 0% and 100% + #[clap(long, default_value_t = 0)] + pub(crate) commission_percentage: u64, + + /// Whether the validator will be joining the genesis validator set + /// + /// If set this validator will already be in the validator set at genesis + #[clap(long)] + pub(crate) join_during_genesis: bool, + + /// Path to private identity generated from GenerateKeys + #[clap(long, parse(from_os_str))] + pub(crate) owner_public_identity_file: Option, + + /// Path to operator public identity, defaults to owner identity + #[clap(long, parse(from_os_str))] + pub(crate) operator_public_identity_file: Option, + + /// Path to voter public identity, defaults to owner identity + #[clap(long, parse(from_os_str))] + pub(crate) voter_public_identity_file: Option, + + #[clap(flatten)] + pub(crate) git_options: GitOptions, +} + +#[async_trait] +impl CliCommand<()> for SetValidatorConfiguration { + fn command_name(&self) -> &'static str { + "SetValidatorConfiguration" + } + + async fn execute(self) -> CliTypedResult<()> { + // Load owner + let owner_keys_file = if let Some(owner_keys_file) = self.owner_public_identity_file { + owner_keys_file + } else { + current_dir()?.join(PUBLIC_KEYS_FILE) + }; + let owner_identity = read_public_identity_file(owner_keys_file.as_path())?; + + // Load voter + let voter_identity = if let Some(voter_keys_file) = self.voter_public_identity_file { + read_public_identity_file(voter_keys_file.as_path())? + } else { + owner_identity.clone() + }; + + // Load operator + let (operator_identity, operator_keys_file) = + if let Some(operator_keys_file) = self.operator_public_identity_file { + ( + read_public_identity_file(operator_keys_file.as_path())?, + operator_keys_file, + ) + } else { + (owner_identity.clone(), owner_keys_file) + }; + + // Extract the possible optional fields + let consensus_public_key = + if let Some(consensus_public_key) = operator_identity.consensus_public_key { + consensus_public_key + } else { + return Err(CliError::CommandArgumentError(format!( + "Failed to read consensus public key from public identity file {}", + operator_keys_file.display() + ))); + }; + + let validator_network_public_key = if let Some(validator_network_public_key) = + operator_identity.validator_network_public_key + { + validator_network_public_key + } else { + return Err(CliError::CommandArgumentError(format!( + "Failed to read validator network public key from public identity file {}", + operator_keys_file.display() + ))); + }; + + let consensus_proof_of_possession = if let Some(consensus_proof_of_possession) = + operator_identity.consensus_proof_of_possession + { + consensus_proof_of_possession + } else { + return Err(CliError::CommandArgumentError(format!( + "Failed to read consensus proof of possession from public identity file {}", + operator_keys_file.display() + ))); + }; + + // Only add the public key if there is a full node + let full_node_network_public_key = if self.full_node_host.is_some() { + operator_identity.full_node_network_public_key + } else { + None + }; + + // Build operator configuration file + let operator_config = OperatorConfiguration { + operator_account_address: operator_identity.account_address.into(), + operator_account_public_key: operator_identity.account_public_key.clone(), + consensus_public_key, + consensus_proof_of_possession, + validator_network_public_key, + validator_host: self.validator_host, + full_node_network_public_key, + full_node_host: self.full_node_host, + }; + + let owner_config = OwnerConfiguration { + owner_account_address: owner_identity.account_address.into(), + owner_account_public_key: owner_identity.account_public_key, + voter_account_address: voter_identity.account_address.into(), + voter_account_public_key: voter_identity.account_public_key, + operator_account_address: operator_identity.account_address.into(), + operator_account_public_key: operator_identity.account_public_key, + stake_amount: self.stake_amount, + commission_percentage: self.commission_percentage, + join_during_genesis: self.join_during_genesis, + }; + + let directory = PathBuf::from(&self.username); + let operator_file = directory.join(OPERATOR_FILE); + let owner_file = directory.join(OWNER_FILE); + + let git_client = self.git_options.get_client()?; + git_client.put(operator_file.as_path(), &operator_config)?; + git_client.put(owner_file.as_path(), &owner_config) + } +} + +pub fn read_public_identity_file(public_identity_file: &Path) -> CliTypedResult { + let bytes = read_from_file(public_identity_file)?; + from_yaml(&String::from_utf8(bytes).map_err(CliError::from)?) +} + +/// Generate a Layout template file +/// +/// This will generate a layout template file for genesis with some default values. To start a +/// new chain, these defaults should be carefully thought through and chosen. +#[derive(Parser)] +pub struct GenerateLayoutTemplate { + /// Path of the output layout template + #[clap(long, parse(from_os_str), default_value = LAYOUT_FILE)] + pub(crate) output_file: PathBuf, + + #[clap(flatten)] + pub(crate) prompt_options: PromptOptions, +} + +#[async_trait] +impl CliCommand<()> for GenerateLayoutTemplate { + fn command_name(&self) -> &'static str { + "GenerateLayoutTemplate" + } + + async fn execute(self) -> CliTypedResult<()> { + check_if_file_exists(self.output_file.as_path(), self.prompt_options)?; + let layout = Layout::default(); + + write_to_user_only_file( + self.output_file.as_path(), + &self.output_file.display().to_string(), + to_yaml(&layout)?.as_bytes(), + ) + } +} + +/// Generate a WriteSet genesis +/// +/// This will compile a Move script and generate a writeset from that script. +#[derive(Parser)] +pub struct GenerateAdminWriteSet { + /// Path of the output genesis file + #[clap(long, parse(from_os_str))] + pub(crate) output_file: PathBuf, + + /// Address of the account which execute this script. + #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] + pub(crate) execute_as: AccountAddress, + + #[clap(flatten)] + pub(crate) compile_proposal_args: CompileScriptFunction, + + #[clap(flatten)] + pub(crate) prompt_options: PromptOptions, +} + +#[async_trait] +impl CliCommand<()> for GenerateAdminWriteSet { + fn command_name(&self) -> &'static str { + "GenerateAdminWriteSet" + } + + async fn execute(self) -> CliTypedResult<()> { + check_if_file_exists(self.output_file.as_path(), self.prompt_options)?; + let (bytecode, _script_hash) = self + .compile_proposal_args + .compile("GenerateAdminWriteSet", self.prompt_options)?; + + let txn = Transaction::GenesisTransaction(WriteSetPayload::Script { + execute_as: self.execute_as, + script: Script::new(bytecode, vec![], vec![]), + }); + + write_to_user_only_file( + self.output_file.as_path(), + &self.output_file.display().to_string(), + &bcs::to_bytes(&txn).map_err(CliError::from)?, + ) + } +} diff --git a/movement-sdk/clis/aptos/src/genesis/mod.rs b/movement-sdk/clis/aptos/src/genesis/mod.rs new file mode 100644 index 000000000..66f1a5b6c --- /dev/null +++ b/movement-sdk/clis/aptos/src/genesis/mod.rs @@ -0,0 +1,926 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +pub mod git; +pub mod keys; +#[cfg(test)] +mod tests; +pub mod tools; + +use crate::{ + common::{ + types::{CliError, CliTypedResult, PromptOptions}, + utils::{check_if_file_exists, dir_default_to_current, write_to_file}, + }, + genesis::git::{ + Client, GitOptions, BALANCES_FILE, EMPLOYEE_VESTING_ACCOUNTS_FILE, LAYOUT_FILE, + OPERATOR_FILE, OWNER_FILE, + }, + CliCommand, CliResult, +}; +use aptos_crypto::{ + bls12381, ed25519::ED25519_PUBLIC_KEY_LENGTH, x25519, ValidCryptoMaterial, + ValidCryptoMaterialStringExt, +}; +use aptos_genesis::{ + builder::GenesisConfiguration, + config::{ + AccountBalanceMap, EmployeePoolMap, HostAndPort, Layout, StringOperatorConfiguration, + StringOwnerConfiguration, ValidatorConfiguration, + }, + mainnet::MainnetGenesisInfo, + GenesisInfo, +}; +use aptos_logger::info; +use aptos_types::{ + account_address::{AccountAddress, AccountAddressWithChecks}, + on_chain_config::{OnChainConsensusConfig, OnChainExecutionConfig}, +}; +use aptos_vm_genesis::{default_gas_schedule, AccountBalance, EmployeePool}; +use async_trait::async_trait; +use clap::Parser; +use std::{ + cmp::Ordering, + collections::{BTreeMap, BTreeSet, HashSet}, + path::{Path, PathBuf}, + str::FromStr, +}; + +const WAYPOINT_FILE: &str = "waypoint.txt"; +const GENESIS_FILE: &str = "genesis.blob"; + +/// Tool for setting up an Movement chain Genesis transaction +/// +/// This tool sets up a space for multiple initial "validator" +/// accounts to build a genesis transaction for a new chain. +#[derive(Parser)] +pub enum GenesisTool { + GenerateAdminWriteSet(keys::GenerateAdminWriteSet), + GenerateGenesis(GenerateGenesis), + GetPoolAddresses(tools::PoolAddresses), + GenerateKeys(keys::GenerateKeys), + GenerateLayoutTemplate(keys::GenerateLayoutTemplate), + SetupGit(git::SetupGit), + SetValidatorConfiguration(keys::SetValidatorConfiguration), +} + +impl GenesisTool { + pub async fn execute(self) -> CliResult { + match self { + GenesisTool::GenerateAdminWriteSet(tool) => tool.execute_serialized_success().await, + GenesisTool::GenerateGenesis(tool) => tool.execute_serialized().await, + GenesisTool::GetPoolAddresses(tool) => tool.execute_serialized().await, + GenesisTool::GenerateKeys(tool) => tool.execute_serialized().await, + GenesisTool::GenerateLayoutTemplate(tool) => tool.execute_serialized_success().await, + GenesisTool::SetupGit(tool) => tool.execute_serialized_success().await, + GenesisTool::SetValidatorConfiguration(tool) => tool.execute_serialized_success().await, + } + } +} + +/// Generate genesis from a git repository +/// +/// This will create a genesis.blob and a waypoint.txt to be used for +/// running a network +#[derive(Parser)] +pub struct GenerateGenesis { + /// Output directory for Genesis file and waypoint + #[clap(long, parse(from_os_str))] + output_dir: Option, + /// Whether this is mainnet genesis. + /// + /// Default is false + #[clap(long)] + mainnet: bool, + + #[clap(flatten)] + prompt_options: PromptOptions, + #[clap(flatten)] + git_options: GitOptions, +} + +#[async_trait] +impl CliCommand> for GenerateGenesis { + fn command_name(&self) -> &'static str { + "GenerateGenesis" + } + + async fn execute(self) -> CliTypedResult> { + let output_dir = dir_default_to_current(self.output_dir.clone())?; + let genesis_file = output_dir.join(GENESIS_FILE); + let waypoint_file = output_dir.join(WAYPOINT_FILE); + check_if_file_exists(genesis_file.as_path(), self.prompt_options)?; + check_if_file_exists(waypoint_file.as_path(), self.prompt_options)?; + + // Generate genesis and waypoint files + let (genesis_bytes, waypoint) = if self.mainnet { + let mut mainnet_genesis = fetch_mainnet_genesis_info(self.git_options)?; + let genesis_bytes = bcs::to_bytes(mainnet_genesis.clone().get_genesis()) + .map_err(|e| CliError::BCS(GENESIS_FILE, e))?; + (genesis_bytes, mainnet_genesis.generate_waypoint()?) + } else { + let mut test_genesis = fetch_genesis_info(self.git_options)?; + let genesis_bytes = bcs::to_bytes(test_genesis.clone().get_genesis()) + .map_err(|e| CliError::BCS(GENESIS_FILE, e))?; + (genesis_bytes, test_genesis.generate_waypoint()?) + }; + write_to_file(genesis_file.as_path(), GENESIS_FILE, &genesis_bytes)?; + write_to_file( + waypoint_file.as_path(), + WAYPOINT_FILE, + waypoint.to_string().as_bytes(), + )?; + Ok(vec![genesis_file, waypoint_file]) + } +} + +/// Retrieves all information for mainnet genesis from the Git repository +pub fn fetch_mainnet_genesis_info(git_options: GitOptions) -> CliTypedResult { + let client = git_options.get_client()?; + let layout: Layout = client.get(Path::new(LAYOUT_FILE))?; + + if layout.root_key.is_some() { + return Err(CliError::UnexpectedError( + "Root key must not be set for mainnet.".to_string(), + )); + } + + let total_supply = layout.total_supply.ok_or_else(|| { + CliError::UnexpectedError("Layout file does not have `total_supply`".to_string()) + })?; + + let account_balance_map: AccountBalanceMap = client.get(Path::new(BALANCES_FILE))?; + let accounts: Vec = account_balance_map.try_into()?; + + // Check that the supply matches the total + let total_balance_supply: u64 = accounts.iter().map(|inner| inner.balance).sum(); + if total_supply != total_balance_supply { + return Err(CliError::UnexpectedError(format!( + "Total supply seen {} doesn't match expected total supply {}", + total_balance_supply, total_supply + ))); + } + + // Check that the user has a reasonable amount of APT, since below the minimum gas amount is + // not useful 1 APT minimally + const MIN_USEFUL_AMOUNT: u64 = 200000000; + let ten_percent_of_total = total_supply / 10; + for account in accounts.iter() { + if account.balance != 0 && account.balance < MIN_USEFUL_AMOUNT { + return Err(CliError::UnexpectedError(format!( + "Account {} has an initial supply below expected amount {} < {}", + account.account_address, account.balance, MIN_USEFUL_AMOUNT + ))); + } else if account.balance > ten_percent_of_total { + return Err(CliError::UnexpectedError(format!( + "Account {} has an more than 10% of the total balance {} > {}", + account.account_address, account.balance, ten_percent_of_total + ))); + } + } + + // Keep track of accounts for later lookup of balances + let initialized_accounts: BTreeMap = accounts + .iter() + .map(|inner| (inner.account_address, inner.balance)) + .collect(); + + let employee_vesting_accounts: EmployeePoolMap = + client.get(Path::new(EMPLOYEE_VESTING_ACCOUNTS_FILE))?; + + let employee_validators: Vec<_> = employee_vesting_accounts + .inner + .iter() + .map(|inner| inner.validator.clone()) + .collect(); + let employee_vesting_accounts: Vec = employee_vesting_accounts.try_into()?; + let validators = get_validator_configs(&client, &layout, true).map_err(parse_error)?; + let mut unique_accounts = BTreeSet::new(); + let mut unique_network_keys = HashSet::new(); + let mut unique_consensus_keys = HashSet::new(); + let mut unique_consensus_pop = HashSet::new(); + let mut unique_hosts = HashSet::new(); + + validate_employee_accounts( + &employee_vesting_accounts, + &initialized_accounts, + &mut unique_accounts, + )?; + + let mut seen_owners = BTreeMap::new(); + validate_validators( + &layout, + &employee_validators, + &initialized_accounts, + &mut unique_accounts, + &mut unique_network_keys, + &mut unique_consensus_keys, + &mut unique_consensus_pop, + &mut unique_hosts, + &mut seen_owners, + true, + )?; + validate_validators( + &layout, + &validators, + &initialized_accounts, + &mut unique_accounts, + &mut unique_network_keys, + &mut unique_consensus_keys, + &mut unique_consensus_pop, + &mut unique_hosts, + &mut seen_owners, + false, + )?; + + let framework = client.get_framework()?; + Ok(MainnetGenesisInfo::new( + layout.chain_id, + accounts, + employee_vesting_accounts, + validators, + framework, + &GenesisConfiguration { + allow_new_validators: true, + epoch_duration_secs: layout.epoch_duration_secs, + is_test: false, + min_stake: layout.min_stake, + min_voting_threshold: layout.min_voting_threshold, + max_stake: layout.max_stake, + recurring_lockup_duration_secs: layout.recurring_lockup_duration_secs, + required_proposer_stake: layout.required_proposer_stake, + rewards_apy_percentage: layout.rewards_apy_percentage, + voting_duration_secs: layout.voting_duration_secs, + voting_power_increase_limit: layout.voting_power_increase_limit, + employee_vesting_start: layout.employee_vesting_start, + employee_vesting_period_duration: layout.employee_vesting_period_duration, + consensus_config: OnChainConsensusConfig::default(), + execution_config: OnChainExecutionConfig::default(), + gas_schedule: default_gas_schedule(), + }, + )?) +} + +/// Retrieves all information for genesis from the Git repository +pub fn fetch_genesis_info(git_options: GitOptions) -> CliTypedResult { + let client = git_options.get_client()?; + let layout: Layout = client.get(Path::new(LAYOUT_FILE))?; + + if layout.root_key.is_none() { + return Err(CliError::UnexpectedError( + "Layout field root_key was not set. Please provide a hex encoded Ed25519PublicKey." + .to_string(), + )); + } + + let validators = get_validator_configs(&client, &layout, false).map_err(parse_error)?; + let framework = client.get_framework()?; + Ok(GenesisInfo::new( + layout.chain_id, + layout.root_key.unwrap(), + validators, + framework, + &GenesisConfiguration { + allow_new_validators: layout.allow_new_validators, + epoch_duration_secs: layout.epoch_duration_secs, + is_test: layout.is_test, + min_stake: layout.min_stake, + min_voting_threshold: layout.min_voting_threshold, + max_stake: layout.max_stake, + recurring_lockup_duration_secs: layout.recurring_lockup_duration_secs, + required_proposer_stake: layout.required_proposer_stake, + rewards_apy_percentage: layout.rewards_apy_percentage, + voting_duration_secs: layout.voting_duration_secs, + voting_power_increase_limit: layout.voting_power_increase_limit, + employee_vesting_start: layout.employee_vesting_start, + employee_vesting_period_duration: layout.employee_vesting_period_duration, + consensus_config: OnChainConsensusConfig::default(), + execution_config: OnChainExecutionConfig::default(), + gas_schedule: default_gas_schedule(), + }, + )?) +} + +fn parse_error(errors: Vec) -> CliError { + eprintln!( + "Failed to parse genesis inputs:\n{}", + serde_yaml::to_string(&errors).unwrap() + ); + CliError::UnexpectedError("Failed to parse genesis inputs".to_string()) +} + +fn get_validator_configs( + client: &Client, + layout: &Layout, + is_mainnet: bool, +) -> Result, Vec> { + let mut validators = Vec::new(); + let mut errors = Vec::new(); + for user in &layout.users { + match get_config(client, user, is_mainnet) { + Ok(validator) => { + validators.push(validator); + }, + Err(failure) => { + if let CliError::UnexpectedError(failure) = failure { + errors.push(format!("{}: {}", user, failure)); + } else { + errors.push(format!("{}: {:?}", user, failure)); + } + }, + } + } + + if errors.is_empty() { + Ok(validators) + } else { + Err(errors) + } +} + +/// Do proper parsing so more information is known about failures +fn get_config( + client: &Client, + user: &str, + is_mainnet: bool, +) -> CliTypedResult { + // Load a user's configuration files + let dir = PathBuf::from(user); + let owner_file = dir.join(OWNER_FILE); + let owner_file = owner_file.as_path(); + let owner_config = client.get::(owner_file)?; + + // Check and convert fields in owner file + let owner_account_address: AccountAddress = parse_required_option( + &owner_config.owner_account_address, + owner_file, + "owner_account_address", + AccountAddressWithChecks::from_str, + )? + .into(); + let owner_account_public_key = parse_required_option( + &owner_config.owner_account_public_key, + owner_file, + "owner_account_public_key", + |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), + )?; + + let operator_account_address: AccountAddress = parse_required_option( + &owner_config.operator_account_address, + owner_file, + "operator_account_address", + AccountAddressWithChecks::from_str, + )? + .into(); + let operator_account_public_key = parse_required_option( + &owner_config.operator_account_public_key, + owner_file, + "operator_account_public_key", + |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), + )?; + + let voter_account_address: AccountAddress = parse_required_option( + &owner_config.voter_account_address, + owner_file, + "voter_account_address", + AccountAddressWithChecks::from_str, + )? + .into(); + let voter_account_public_key = parse_required_option( + &owner_config.voter_account_public_key, + owner_file, + "voter_account_public_key", + |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), + )?; + + let stake_amount = parse_required_option( + &owner_config.stake_amount, + owner_file, + "stake_amount", + u64::from_str, + )?; + + // Default to 0 for commission percentage if missing. + let commission_percentage = parse_optional_option( + &owner_config.commission_percentage, + owner_file, + "commission_percentage", + u64::from_str, + )? + .unwrap_or(0); + + // Default to true for whether the validator should be joining during genesis. + let join_during_genesis = parse_optional_option( + &owner_config.join_during_genesis, + owner_file, + "join_during_genesis", + bool::from_str, + )? + .unwrap_or(true); + + // We don't require the operator file if the validator is not joining during genesis. + if is_mainnet && !join_during_genesis { + return Ok(ValidatorConfiguration { + owner_account_address: owner_account_address.into(), + owner_account_public_key, + operator_account_address: operator_account_address.into(), + operator_account_public_key, + voter_account_address: voter_account_address.into(), + voter_account_public_key, + consensus_public_key: None, + proof_of_possession: None, + validator_network_public_key: None, + validator_host: None, + full_node_network_public_key: None, + full_node_host: None, + stake_amount, + commission_percentage, + join_during_genesis, + }); + }; + + let operator_file = dir.join(OPERATOR_FILE); + let operator_file = operator_file.as_path(); + let operator_config = client.get::(operator_file)?; + + // Check and convert fields in operator file + let operator_account_address_from_file: AccountAddress = parse_required_option( + &operator_config.operator_account_address, + operator_file, + "operator_account_address", + AccountAddressWithChecks::from_str, + )? + .into(); + let operator_account_public_key_from_file = parse_required_option( + &operator_config.operator_account_public_key, + operator_file, + "operator_account_public_key", + |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), + )?; + let consensus_public_key = parse_required_option( + &operator_config.consensus_public_key, + operator_file, + "consensus_public_key", + |str| parse_key(bls12381::PublicKey::LENGTH, str), + )?; + let consensus_proof_of_possession = parse_required_option( + &operator_config.consensus_proof_of_possession, + operator_file, + "consensus_proof_of_possession", + |str| parse_key(bls12381::ProofOfPossession::LENGTH, str), + )?; + let validator_network_public_key = parse_required_option( + &operator_config.validator_network_public_key, + operator_file, + "validator_network_public_key", + |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), + )?; + let full_node_network_public_key = parse_optional_option( + &operator_config.full_node_network_public_key, + operator_file, + "full_node_network_public_key", + |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), + )?; + + // Verify owner & operator agree on operator + if operator_account_address != operator_account_address_from_file { + return Err( + CliError::CommandArgumentError( + format!("Operator account {} in owner file {} does not match operator account {} in operator file {}", + operator_account_address, + owner_file.display(), + operator_account_address_from_file, + operator_file.display() + ))); + } + if operator_account_public_key != operator_account_public_key_from_file { + return Err( + CliError::CommandArgumentError( + format!("Operator public key {} in owner file {} does not match operator public key {} in operator file {}", + operator_account_public_key, + owner_file.display(), + operator_account_public_key_from_file, + operator_file.display() + ))); + } + + // Build Validator configuration + Ok(ValidatorConfiguration { + owner_account_address: owner_account_address.into(), + owner_account_public_key, + operator_account_address: operator_account_address.into(), + operator_account_public_key, + voter_account_address: voter_account_address.into(), + voter_account_public_key, + consensus_public_key: Some(consensus_public_key), + proof_of_possession: Some(consensus_proof_of_possession), + validator_network_public_key: Some(validator_network_public_key), + validator_host: Some(operator_config.validator_host), + full_node_network_public_key, + full_node_host: operator_config.full_node_host, + stake_amount, + commission_percentage, + join_during_genesis, + }) +} + +// TODO: Move into the Crypto libraries +fn parse_key(num_bytes: usize, str: &str) -> anyhow::Result { + let num_chars: usize = num_bytes * 2; + let mut working = str.trim(); + + // Checks if it has a 0x at the beginning, which is okay + if working.starts_with("0x") { + working = &working[2..]; + } + + match working.len().cmp(&num_chars) { + Ordering::Less => { + anyhow::bail!( + "Key {} is too short {} must be {} hex characters", + str, + working.len(), + num_chars + ) + }, + Ordering::Greater => { + anyhow::bail!( + "Key {} is too long {} must be {} hex characters with or without a 0x in front", + str, + working.len(), + num_chars + ) + }, + Ordering::Equal => {}, + } + + if !working.chars().all(|c| char::is_ascii_hexdigit(&c)) { + anyhow::bail!("Key {} contains a non-hex character", str) + } + + Ok(T::from_encoded_string(str.trim())?) +} + +fn parse_required_option Result, T, E: std::fmt::Display>( + option: &Option, + file: &Path, + field_name: &'static str, + parse: F, +) -> Result { + if let Some(ref field) = option { + parse(field).map_err(|err| { + CliError::CommandArgumentError(format!( + "Field {} is invalid in file {}. Err: {}", + field_name, + file.display(), + err + )) + }) + } else { + Err(CliError::CommandArgumentError(format!( + "File {} is missing {}", + file.display(), + field_name + ))) + } +} + +fn parse_optional_option Result, T, E: std::fmt::Display>( + option: &Option, + file: &Path, + field_name: &'static str, + parse: F, +) -> Result, CliError> { + if let Some(ref field) = option { + parse(field) + .map_err(|err| { + CliError::CommandArgumentError(format!( + "Field {} is invalid in file {}. Err: {}", + field_name, + file.display(), + err + )) + }) + .map(Some) + } else { + Ok(None) + } +} + +fn validate_validators( + layout: &Layout, + validators: &[ValidatorConfiguration], + initialized_accounts: &BTreeMap, + unique_accounts: &mut BTreeSet, + unique_network_keys: &mut HashSet, + unique_consensus_keys: &mut HashSet, + unique_consensus_pops: &mut HashSet, + unique_hosts: &mut HashSet, + seen_owners: &mut BTreeMap, + is_pooled_validator: bool, +) -> CliTypedResult<()> { + // check accounts for validators + let mut errors = vec![]; + + for (i, validator) in validators.iter().enumerate() { + let name = if is_pooled_validator { + format!("Employee Pool #{}", i) + } else { + layout.users.get(i).unwrap().to_string() + }; + + if !initialized_accounts.contains_key(&validator.owner_account_address.into()) { + errors.push(CliError::UnexpectedError(format!( + "Owner {} in validator {} is is not in the balances.yaml file", + validator.owner_account_address, name + ))); + } + if !initialized_accounts.contains_key(&validator.operator_account_address.into()) { + errors.push(CliError::UnexpectedError(format!( + "Operator {} in validator {} is is not in the balances.yaml file", + validator.operator_account_address, name + ))); + } + if !initialized_accounts.contains_key(&validator.voter_account_address.into()) { + errors.push(CliError::UnexpectedError(format!( + "Voter {} in validator {} is is not in the balances.yaml file", + validator.voter_account_address, name + ))); + } + + let owner_balance = initialized_accounts + .get(&validator.owner_account_address.into()) + .unwrap(); + + if seen_owners.contains_key(&validator.owner_account_address.into()) { + errors.push(CliError::UnexpectedError(format!( + "Owner {} in validator {} has been seen before as an owner of validator {}", + validator.owner_account_address, + name, + seen_owners + .get(&validator.owner_account_address.into()) + .unwrap() + ))); + } + seen_owners.insert(validator.owner_account_address.into(), i); + + if unique_accounts.contains(&validator.owner_account_address.into()) { + errors.push(CliError::UnexpectedError(format!( + "Owner '{}' in validator {} has already been seen elsewhere", + validator.owner_account_address, name + ))); + } + unique_accounts.insert(validator.owner_account_address.into()); + + if unique_accounts.contains(&validator.operator_account_address.into()) { + errors.push(CliError::UnexpectedError(format!( + "Operator '{}' in validator {} has already been seen elsewhere", + validator.operator_account_address, name + ))); + } + unique_accounts.insert(validator.operator_account_address.into()); + + // Pooled validators have a combined balance + // TODO: Make this field optional but checked + if !is_pooled_validator && *owner_balance < validator.stake_amount { + errors.push(CliError::UnexpectedError(format!( + "Owner {} in validator {} has less in it's balance {} than the stake amount for the validator {}", + validator.owner_account_address, name, owner_balance, validator.stake_amount + ))); + } + if validator.stake_amount < layout.min_stake { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has stake {} under the min stake {}", + name, validator.stake_amount, layout.min_stake + ))); + } + if validator.stake_amount > layout.max_stake { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has stake {} over the max stake {}", + name, validator.stake_amount, layout.max_stake + ))); + } + + // Ensure that the validator is setup correctly if it's joining in genesis + if validator.join_during_genesis { + if validator.validator_network_public_key.is_none() { + errors.push(CliError::UnexpectedError(format!( + "Validator {} does not have a validator network public key, though it's joining during genesis", + name + ))); + } + if !unique_network_keys.insert(validator.validator_network_public_key.unwrap()) { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has a repeated validator network key{}", + name, + validator.validator_network_public_key.unwrap() + ))); + } + + if validator.validator_host.is_none() { + errors.push(CliError::UnexpectedError(format!( + "Validator {} does not have a validator host, though it's joining during genesis", + name + ))); + } + if !unique_hosts.insert(validator.validator_host.as_ref().unwrap().clone()) { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has a repeated validator host {:?}", + name, + validator.validator_host.as_ref().unwrap() + ))); + } + + if validator.consensus_public_key.is_none() { + errors.push(CliError::UnexpectedError(format!( + "Validator {} does not have a consensus public key, though it's joining during genesis", + name + ))); + } + if !unique_consensus_keys + .insert(validator.consensus_public_key.as_ref().unwrap().clone()) + { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has a repeated a consensus public key {}", + name, + validator.consensus_public_key.as_ref().unwrap() + ))); + } + + if validator.proof_of_possession.is_none() { + errors.push(CliError::UnexpectedError(format!( + "Validator {} does not have a consensus proof of possession, though it's joining during genesis", + name + ))); + } + if !unique_consensus_pops + .insert(validator.proof_of_possession.as_ref().unwrap().clone()) + { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has a repeated a consensus proof of possessions {}", + name, + validator.proof_of_possession.as_ref().unwrap() + ))); + } + + match ( + validator.full_node_host.as_ref(), + validator.full_node_network_public_key.as_ref(), + ) { + (None, None) => { + info!("Validator {} does not have a full node setup", name); + }, + (Some(_), None) | (None, Some(_)) => { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has a full node host or public key but not both", + name + ))); + }, + (Some(full_node_host), Some(full_node_network_public_key)) => { + // Ensure that the validator and the full node aren't the same + let validator_host = validator.validator_host.as_ref().unwrap(); + let validator_network_public_key = + validator.validator_network_public_key.as_ref().unwrap(); + if validator_host == full_node_host { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has a validator and a full node host that are the same {:?}", + name, + validator_host + ))); + } + if !unique_hosts.insert(validator.full_node_host.as_ref().unwrap().clone()) { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has a repeated full node host {:?}", + name, + validator.full_node_host.as_ref().unwrap() + ))); + } + + if validator_network_public_key == full_node_network_public_key { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has a validator and a full node network public key that are the same {}", + name, + validator_network_public_key + ))); + } + if !unique_network_keys.insert(validator.full_node_network_public_key.unwrap()) + { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has a repeated full node network key {}", + name, + validator.full_node_network_public_key.unwrap() + ))); + } + }, + } + } else { + if validator.validator_network_public_key.is_some() { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has a validator network public key, but it is *NOT* joining during genesis", + name + ))); + } + if validator.validator_host.is_some() { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has a validator host, but it is *NOT* joining during genesis", + name + ))); + } + if validator.consensus_public_key.is_some() { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has a consensus public key, but it is *NOT* joining during genesis", + name + ))); + } + if validator.proof_of_possession.is_some() { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has a consensus proof of possession, but it is *NOT* joining during genesis", + name + ))); + } + if validator.full_node_network_public_key.is_some() { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has a full node public key, but it is *NOT* joining during genesis", + name + ))); + } + if validator.full_node_host.is_some() { + errors.push(CliError::UnexpectedError(format!( + "Validator {} has a full node host, but it is *NOT* joining during genesis", + name + ))); + } + } + } + + if errors.is_empty() { + Ok(()) + } else { + eprintln!("{:#?}", errors); + + Err(CliError::UnexpectedError( + "Failed to validate validators".to_string(), + )) + } +} + +fn validate_employee_accounts( + employee_vesting_accounts: &[EmployeePool], + initialized_accounts: &BTreeMap, + unique_accounts: &mut BTreeSet, +) -> CliTypedResult<()> { + // Check accounts for employee accounts + for (i, pool) in employee_vesting_accounts.iter().enumerate() { + let mut total_stake_pool_amount = 0; + for (j, account) in pool.accounts.iter().enumerate() { + if !initialized_accounts.contains_key(account) { + return Err(CliError::UnexpectedError(format!( + "Account #{} '{}' in employee pool #{} is not in the balances.yaml file", + j, account, i + ))); + } + if unique_accounts.contains(account) { + return Err(CliError::UnexpectedError(format!( + "Account #{} '{}' in employee pool #{} has already been seen elsewhere", + j, account, i + ))); + } + unique_accounts.insert(*account); + + total_stake_pool_amount += initialized_accounts.get(account).unwrap(); + } + + if total_stake_pool_amount != pool.validator.validator.stake_amount { + return Err(CliError::UnexpectedError(format!( + "Stake amount {} in employee pool #{} does not match combined of accounts {}", + pool.validator.validator.stake_amount, i, total_stake_pool_amount + ))); + } + + if !initialized_accounts.contains_key(&pool.validator.validator.owner_address) { + return Err(CliError::UnexpectedError(format!( + "Owner address {} in employee pool #{} is is not in the balances.yaml file", + pool.validator.validator.owner_address, i + ))); + } + if !initialized_accounts.contains_key(&pool.validator.validator.operator_address) { + return Err(CliError::UnexpectedError(format!( + "Operator address {} in employee pool #{} is is not in the balances.yaml file", + pool.validator.validator.operator_address, i + ))); + } + if !initialized_accounts.contains_key(&pool.validator.validator.voter_address) { + return Err(CliError::UnexpectedError(format!( + "Voter address {} in employee pool #{} is is not in the balances.yaml file", + pool.validator.validator.voter_address, i + ))); + } + if !initialized_accounts.contains_key(&pool.beneficiary_resetter) { + return Err(CliError::UnexpectedError(format!( + "Beneficiary resetter {} in employee pool #{} is is not in the balances.yaml file", + pool.beneficiary_resetter, i + ))); + } + } + Ok(()) +} diff --git a/movement-sdk/clis/aptos/src/genesis/tests.rs b/movement-sdk/clis/aptos/src/genesis/tests.rs new file mode 100644 index 000000000..bb270e6ef --- /dev/null +++ b/movement-sdk/clis/aptos/src/genesis/tests.rs @@ -0,0 +1,434 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{ + types::{OptionalPoolAddressArgs, PromptOptions, RngArgs}, + utils::{read_from_file, write_to_file}, + }, + genesis::{ + git::{ + from_yaml, GitOptions, SetupGit, BALANCES_FILE, EMPLOYEE_VESTING_ACCOUNTS_FILE, + FRAMEWORK_NAME, + }, + keys::{GenerateKeys, GenerateLayoutTemplate, SetValidatorConfiguration, PUBLIC_KEYS_FILE}, + GenerateGenesis, + }, + CliCommand, +}; +use aptos_crypto::{ + ed25519::{Ed25519PrivateKey, Ed25519PublicKey}, + PrivateKey, +}; +use aptos_genesis::{ + config::{ + AccountBalanceMap, EmployeePoolConfig, EmployeePoolMap, HostAndPort, Layout, + ValidatorConfiguration, + }, + keys::PublicIdentity, +}; +use aptos_keygen::KeyGen; +use aptos_temppath::TempPath; +use aptos_types::{account_address::AccountAddress, chain_id::ChainId}; +use aptos_vm_genesis::{AccountBalance, TestValidator}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, + str::FromStr, +}; + +const INITIAL_BALANCE: u64 = 100_000_000_000_000; + +/// Test the E2E genesis flow since it doesn't require a node to run +#[tokio::test] +async fn test_genesis_e2e_flow() { + let is_mainnet = false; + let dir = TempPath::new(); + dir.create_as_dir().unwrap(); + let git_options = create_users(2, 0, &dir, &mut vec![], is_mainnet).await; + + // Now generate genesis + let output_dir = TempPath::new(); + output_dir.create_as_dir().unwrap(); + let output_dir = PathBuf::from(output_dir.path()); + generate_genesis(git_options, output_dir.clone(), is_mainnet).await; + + // TODO: Verify that these are good + let waypoint_file = output_dir.join("waypoint.txt"); + assert!(waypoint_file.exists()); + let genesis_file = output_dir.join("genesis.blob"); + assert!(genesis_file.exists()); +} + +#[tokio::test] +async fn test_mainnet_genesis_e2e_flow() { + let is_mainnet = true; + let dir = TempPath::new(); + dir.create_as_dir().unwrap(); + let git_options = create_users(2, 4, &dir, &mut vec![10, 1], is_mainnet).await; + let account_1 = AccountAddress::from_hex_literal("0x101").unwrap(); + let account_2 = AccountAddress::from_hex_literal("0x102").unwrap(); + let employee_1 = AccountAddress::from_hex_literal("0x201").unwrap(); + let employee_2 = AccountAddress::from_hex_literal("0x202").unwrap(); + let employee_3 = AccountAddress::from_hex_literal("0x203").unwrap(); + let employee_4 = AccountAddress::from_hex_literal("0x204").unwrap(); + + let owner_identity1 = load_identity(dir.path(), "owner-0"); + let owner_identity2 = load_identity(dir.path(), "owner-1"); + let operator_identity1 = load_identity(dir.path(), "operator-0"); + let operator_identity2 = load_identity(dir.path(), "operator-1"); + let voter_identity1 = load_identity(dir.path(), "voter-0"); + let voter_identity2 = load_identity(dir.path(), "voter-1"); + let admin_identity1 = load_identity(dir.path(), "other-0"); + let admin_identity2 = load_identity(dir.path(), "other-1"); + let employee_operator_identity1 = load_identity(dir.path(), "other-2"); + let employee_operator_identity2 = load_identity(dir.path(), "other-3"); + + // Create initial balances and employee vesting account files. + let git_dir = git_options.local_repository_dir.as_ref().unwrap().as_path(); + + create_account_balances_file(PathBuf::from(git_dir), vec![ + owner_identity1.account_address, + owner_identity2.account_address, + operator_identity1.account_address, + operator_identity2.account_address, + voter_identity1.account_address, + voter_identity2.account_address, + account_1, + account_2, + employee_1, + employee_2, + employee_3, + employee_4, + admin_identity1.account_address, + admin_identity2.account_address, + employee_operator_identity1.account_address, + employee_operator_identity2.account_address, + ]) + .await; + create_employee_vesting_accounts_file( + PathBuf::from(git_dir), + &[admin_identity1, admin_identity2], + &[employee_operator_identity1, employee_operator_identity2], + &[vec![employee_1, employee_2], vec![employee_3, employee_4]], + &[true, false], + ) + .await; + + // Now generate genesis + let output_dir = TempPath::new(); + output_dir.create_as_dir().unwrap(); + let output_dir = PathBuf::from(output_dir.path()); + generate_genesis(git_options, output_dir.clone(), is_mainnet).await; + + // TODO: Verify that these are good + let waypoint_file = output_dir.join("waypoint.txt"); + assert!(waypoint_file.exists()); + let genesis_file = output_dir.join("genesis.blob"); + assert!(genesis_file.exists()); +} + +pub fn load_identity(base_dir: &Path, name: &str) -> PublicIdentity { + let path = base_dir.join(name).join(PUBLIC_KEYS_FILE); + from_yaml(&String::from_utf8(read_from_file(path.as_path()).unwrap()).unwrap()).unwrap() +} + +async fn create_users( + num_validators: u8, + num_other_users: u8, + dir: &TempPath, + commission_rates: &mut Vec, + is_mainnet: bool, +) -> GitOptions { + let mut users: HashMap = HashMap::new(); + for i in 0..num_validators { + let name = format!("owner-{}", i); + let output_dir = generate_keys(dir.path(), &name).await; + users.insert(name, output_dir); + + let name = format!("operator-{}", i); + let output_dir = generate_keys(dir.path(), &name).await; + users.insert(name, output_dir); + + let name = format!("voter-{}", i); + let output_dir = generate_keys(dir.path(), &name).await; + users.insert(name, output_dir); + } + for i in 0..num_other_users { + let name = format!("other-{}", i); + let output_dir = generate_keys(dir.path(), &name).await; + users.insert(name, output_dir); + } + + // Get the validator's names + let validator_names = users + .keys() + .map(|key| key.to_string()) + .filter(|name| name.starts_with("owner")) + .collect(); + let mut key_gen = KeyGen::from_seed([num_validators.saturating_add(1); 32]); + + // First step is setup the local git repo + let root_private_key = if !is_mainnet { + Some(key_gen.generate_ed25519_private_key()) + } else { + None + }; + let git_options = + setup_git_dir(root_private_key.as_ref(), validator_names, ChainId::test()).await; + + // Only write validators to folders + for i in 0..num_validators { + let owner_name = format!("owner-{}", i); + let owner_identity = users.get(&owner_name).unwrap().join(PUBLIC_KEYS_FILE); + let operator_identity = users + .get(&format!("operator-{}", i)) + .unwrap() + .join(PUBLIC_KEYS_FILE); + let voter_identity = users + .get(&format!("voter-{}", i)) + .unwrap() + .join(PUBLIC_KEYS_FILE); + let commission_rate = if commission_rates.is_empty() { + 0 + } else { + commission_rates.remove(0) + }; + set_validator_config( + owner_name, + git_options.clone(), + owner_identity.as_path(), + operator_identity.as_path(), + voter_identity.as_path(), + commission_rate, + i as u16, + ) + .await; + } + git_options +} + +/// Generate genesis and waypoint +async fn generate_genesis(git_options: GitOptions, output_dir: PathBuf, mainnet: bool) { + let command = GenerateGenesis { + prompt_options: PromptOptions::yes(), + git_options, + output_dir: Some(output_dir), + mainnet, + }; + let _ = command.execute().await.unwrap(); +} + +/// Setup a temporary repo location and add all required pieces +async fn setup_git_dir( + root_private_key: Option<&Ed25519PrivateKey>, + users: Vec, + chain_id: ChainId, +) -> GitOptions { + let git_options = git_options(); + let layout_file = TempPath::new(); + layout_file.create_as_file().unwrap(); + let layout_file = layout_file.path(); + + create_layout_file( + layout_file, + root_private_key.map(|inner| inner.public_key()), + users, + chain_id, + ) + .await; + let setup_command = SetupGit { + git_options: git_options.clone(), + layout_file: PathBuf::from(layout_file), + }; + + setup_command + .execute() + .await + .expect("Should not fail creating repo folder"); + + // Add framework + add_framework_to_dir(git_options.local_repository_dir.as_ref().unwrap().as_path()); + git_options +} + +/// Add framework to git directory +fn add_framework_to_dir(git_dir: &Path) { + aptos_cached_packages::head_release_bundle() + .write(git_dir.join(FRAMEWORK_NAME)) + .unwrap() +} + +/// Local git options for testing +fn git_options() -> GitOptions { + let temp_path = TempPath::new(); + let path = PathBuf::from(temp_path.path()); + GitOptions { + local_repository_dir: Some(path), + ..Default::default() + } +} + +/// Create a layout file for the repo +async fn create_layout_file( + file: &Path, + root_public_key: Option, + users: Vec, + chain_id: ChainId, +) { + GenerateLayoutTemplate { + output_file: PathBuf::from(file), + prompt_options: PromptOptions::yes(), + } + .execute() + .await + .expect("Expected to create layout template"); + + // Update layout file + let mut layout: Layout = + from_yaml(&String::from_utf8(read_from_file(file).unwrap()).unwrap()).unwrap(); + layout.root_key = root_public_key; + layout.users = users; + layout.chain_id = chain_id; + layout.is_test = true; + layout.total_supply = Some(INITIAL_BALANCE * 16); + + write_to_file( + file, + "Layout file", + serde_yaml::to_string(&layout).unwrap().as_bytes(), + ) + .unwrap(); +} + +/// Generate keys for a "user" +async fn generate_keys(dir: &Path, name: &str) -> PathBuf { + let output_dir = dir.join(name); + let command = GenerateKeys { + pool_address_args: OptionalPoolAddressArgs { pool_address: None }, + rng_args: RngArgs::from_string_seed(name), + prompt_options: PromptOptions::yes(), + output_dir: Some(output_dir.clone()), + }; + let _ = command.execute().await.unwrap(); + output_dir +} + +/// Set validator configuration for a user +async fn set_validator_config( + username: String, + git_options: GitOptions, + owner_identity_file: &Path, + operator_identity_file: &Path, + voter_identity_file: &Path, + commission_percentage: u64, + port: u16, +) { + let command = SetValidatorConfiguration { + username, + git_options, + owner_public_identity_file: Some(owner_identity_file.to_path_buf()), + validator_host: HostAndPort::from_str(&format!("localhost:{}", port)).unwrap(), + stake_amount: 100_000_000_000_000, + full_node_host: None, + operator_public_identity_file: Some(operator_identity_file.to_path_buf()), + voter_public_identity_file: Some(voter_identity_file.to_path_buf()), + commission_percentage, + join_during_genesis: true, + }; + + command.execute().await.unwrap() +} + +async fn create_account_balances_file(path: PathBuf, addresses: Vec) { + let account_balances: Vec = addresses + .iter() + .map(|account_address| AccountBalance { + account_address: *account_address, + balance: INITIAL_BALANCE, + }) + .collect(); + + let balance_map = AccountBalanceMap::try_from(account_balances).unwrap(); + + write_to_file( + &path.join(BALANCES_FILE), + BALANCES_FILE, + serde_yaml::to_string(&balance_map).unwrap().as_bytes(), + ) + .unwrap(); +} + +async fn create_employee_vesting_accounts_file( + path: PathBuf, + admin_identities: &[PublicIdentity], + operator_identities: &[PublicIdentity], + employee_groups: &[Vec], + join_during_genesis: &[bool], +) { + TestValidator::new_test_set(Some(employee_groups.len()), Some(INITIAL_BALANCE)); + let employee_vesting_accounts: Vec<_> = employee_groups + .iter() + .enumerate() + .map(|(index, accounts)| { + let admin_identity = admin_identities[index].clone(); + let operator_identity = operator_identities[index].clone(); + let validator_config = if *join_during_genesis.get(index).unwrap() { + ValidatorConfiguration { + owner_account_address: admin_identity.account_address.into(), + owner_account_public_key: admin_identity.account_public_key.clone(), + operator_account_address: operator_identity.account_address.into(), + operator_account_public_key: operator_identity.account_public_key.clone(), + voter_account_address: admin_identity.account_address.into(), + voter_account_public_key: admin_identity.account_public_key, + consensus_public_key: operator_identity.consensus_public_key, + proof_of_possession: operator_identity.consensus_proof_of_possession, + validator_network_public_key: operator_identity.validator_network_public_key, + validator_host: Some(HostAndPort::from_str("localhost:8080").unwrap()), + full_node_network_public_key: operator_identity.full_node_network_public_key, + full_node_host: Some(HostAndPort::from_str("localhost:8081").unwrap()), + stake_amount: 2 * INITIAL_BALANCE, + commission_percentage: 0, + join_during_genesis: true, + } + } else { + ValidatorConfiguration { + owner_account_address: admin_identity.account_address.into(), + owner_account_public_key: admin_identity.account_public_key.clone(), + operator_account_address: operator_identity.account_address.into(), + operator_account_public_key: operator_identity.account_public_key, + voter_account_address: admin_identity.account_address.into(), + voter_account_public_key: admin_identity.account_public_key, + consensus_public_key: None, + proof_of_possession: None, + validator_network_public_key: None, + validator_host: None, + full_node_network_public_key: None, + full_node_host: None, + stake_amount: 2 * INITIAL_BALANCE, + commission_percentage: 0, + join_during_genesis: false, + } + }; + + EmployeePoolConfig { + accounts: accounts.iter().map(|addr| addr.into()).collect(), + validator: validator_config, + vesting_schedule_numerators: vec![3, 3, 3, 3, 1], + vesting_schedule_denominator: 48, + beneficiary_resetter: AccountAddress::from_hex_literal("0x101").unwrap().into(), + } + }) + .collect(); + let employee_vesting_map = EmployeePoolMap { + inner: employee_vesting_accounts, + }; + write_to_file( + &path.join(EMPLOYEE_VESTING_ACCOUNTS_FILE), + EMPLOYEE_VESTING_ACCOUNTS_FILE, + serde_yaml::to_string(&employee_vesting_map) + .unwrap() + .as_bytes(), + ) + .unwrap(); +} diff --git a/movement-sdk/clis/aptos/src/genesis/tools.rs b/movement-sdk/clis/aptos/src/genesis/tools.rs new file mode 100644 index 000000000..5f3f8ed5f --- /dev/null +++ b/movement-sdk/clis/aptos/src/genesis/tools.rs @@ -0,0 +1,100 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{ + types::PromptOptions, + utils::{dir_default_to_current, write_to_file}, + }, + genesis::{ + get_validator_configs, + git::{GitOptions, EMPLOYEE_VESTING_ACCOUNTS_FILE, LAYOUT_FILE}, + parse_error, + }, + CliCommand, CliTypedResult, +}; +use aptos_genesis::config::{EmployeePoolMap, Layout}; +use aptos_sdk::move_types::account_address::AccountAddress; +use aptos_types::account_address::{create_vesting_pool_address, default_stake_pool_address}; +use async_trait::async_trait; +use clap::Parser; +use std::{ + collections::BTreeMap, + path::{Path, PathBuf}, +}; + +const POOL_ADDRESSES: &str = "pool-addresses.yaml"; +const EMPLOYEE_POOL_ADDRESSES: &str = "employee-pool-addresses.yaml"; + +/// Get pool addresses from a mainnet genesis setup +/// +/// Outputs all pool addresses to a file from the genesis files +#[derive(Parser)] +pub struct PoolAddresses { + /// Output directory for pool addresses + #[clap(long, parse(from_os_str))] + output_dir: Option, + + #[clap(flatten)] + prompt_options: PromptOptions, + #[clap(flatten)] + git_options: GitOptions, +} + +#[async_trait] +impl CliCommand> for PoolAddresses { + fn command_name(&self) -> &'static str { + "GetPoolAddresses" + } + + async fn execute(self) -> CliTypedResult> { + let output_dir = dir_default_to_current(self.output_dir.clone())?; + let client = self.git_options.get_client()?; + let layout: Layout = client.get(Path::new(LAYOUT_FILE))?; + let employee_vesting_accounts: EmployeePoolMap = + client.get(Path::new(EMPLOYEE_VESTING_ACCOUNTS_FILE))?; + let validators = get_validator_configs(&client, &layout, true).map_err(parse_error)?; + + let mut address_to_pool = BTreeMap::::new(); + + for validator in validators { + let stake_pool_address = default_stake_pool_address( + validator.owner_account_address.into(), + validator.operator_account_address.into(), + ); + address_to_pool.insert(validator.owner_account_address.into(), stake_pool_address); + } + + let mut employee_address_to_pool = BTreeMap::::new(); + + for employee_pool in employee_vesting_accounts.inner.iter() { + let stake_pool_address = create_vesting_pool_address( + employee_pool.validator.owner_account_address.into(), + employee_pool.validator.operator_account_address.into(), + 0, + &[], + ); + + employee_address_to_pool.insert( + employee_pool.validator.owner_account_address.into(), + stake_pool_address, + ); + } + + let pool_addresses_file = output_dir.join(POOL_ADDRESSES); + let employee_pool_addresses_file = output_dir.join(EMPLOYEE_POOL_ADDRESSES); + + write_to_file( + pool_addresses_file.as_path(), + POOL_ADDRESSES, + serde_yaml::to_string(&address_to_pool)?.as_bytes(), + )?; + write_to_file( + employee_pool_addresses_file.as_path(), + EMPLOYEE_POOL_ADDRESSES, + serde_yaml::to_string(&employee_address_to_pool)?.as_bytes(), + )?; + + Ok(vec![pool_addresses_file, employee_pool_addresses_file]) + } +} diff --git a/movement-sdk/clis/aptos/src/governance/mod.rs b/movement-sdk/clis/aptos/src/governance/mod.rs new file mode 100644 index 000000000..b399696a6 --- /dev/null +++ b/movement-sdk/clis/aptos/src/governance/mod.rs @@ -0,0 +1,1061 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(feature = "no-upload-proposal")] +use crate::common::utils::read_from_file; +use crate::{ + common::{ + types::{ + CliError, CliTypedResult, MovePackageDir, PoolAddressArgs, ProfileOptions, + PromptOptions, RestOptions, TransactionOptions, TransactionSummary, + }, + utils::prompt_yes_with_override, + }, + move_tool::{FrameworkPackageArgs, IncludedArtifacts}, + CliCommand, CliResult, +}; +use aptos_cached_packages::aptos_stdlib; +use aptos_crypto::HashValue; +use aptos_framework::{BuildOptions, BuiltPackage, ReleasePackage}; +use aptos_logger::warn; +use aptos_rest_client::{ + aptos_api_types::{Address, HexEncodedBytes, U128, U64}, + Client, Transaction, +}; +use aptos_sdk::move_types::language_storage::CORE_CODE_ADDRESS; +use aptos_types::{ + account_address::AccountAddress, + event::EventHandle, + governance::VotingRecords, + stake_pool::StakePool, + state_store::table::TableHandle, + transaction::{Script, TransactionPayload}, +}; +use async_trait::async_trait; +use clap::Parser; +use move_core_types::transaction_argument::TransactionArgument; +use reqwest::Url; +use serde::{Deserialize, Serialize}; +use std::{ + collections::BTreeMap, + fmt::Formatter, + fs, + path::{Path, PathBuf}, +}; +use tempfile::TempDir; + +/// Tool for on-chain governance +/// +/// This tool allows voters that have stake to vote the ability to +/// propose changes to the chain, as well as vote and execute these +/// proposals. +#[derive(Parser)] +pub enum GovernanceTool { + Propose(SubmitProposal), + Vote(SubmitVote), + ShowProposal(ViewProposal), + ListProposals(ListProposals), + VerifyProposal(VerifyProposal), + ExecuteProposal(ExecuteProposal), + GenerateUpgradeProposal(GenerateUpgradeProposal), + ApproveExecutionHash(ApproveExecutionHash), +} + +impl GovernanceTool { + pub async fn execute(self) -> CliResult { + use GovernanceTool::*; + match self { + Propose(tool) => tool.execute_serialized().await, + Vote(tool) => tool.execute_serialized().await, + ExecuteProposal(tool) => tool.execute_serialized().await, + GenerateUpgradeProposal(tool) => tool.execute_serialized_success().await, + ShowProposal(tool) => tool.execute_serialized().await, + ListProposals(tool) => tool.execute_serialized().await, + VerifyProposal(tool) => tool.execute_serialized().await, + ApproveExecutionHash(tool) => tool.execute_serialized().await, + } + } +} + +/// View a known on-chain governance proposal +/// +/// This command will return the proposal requested as well as compute +/// the hash of the metadata to determine whether it was verified or not. +#[derive(Parser)] +pub struct ViewProposal { + /// The identifier of the onchain governance proposal + #[clap(long)] + proposal_id: u64, + + #[clap(flatten)] + rest_options: RestOptions, + #[clap(flatten)] + profile: ProfileOptions, +} + +#[async_trait] +impl CliCommand for ViewProposal { + fn command_name(&self) -> &'static str { + "ViewProposal" + } + + async fn execute(mut self) -> CliTypedResult { + // Get proposal + let client = self.rest_options.client(&self.profile)?; + let forum = client + .get_account_resource_bcs::( + AccountAddress::ONE, + "0x1::voting::VotingForum<0x1::governance_proposal::GovernanceProposal>", + ) + .await? + .into_inner(); + let voting_table = forum.table_handle.0; + + let proposal: Proposal = get_proposal(&client, voting_table, self.proposal_id) + .await? + .into(); + + let metadata_hash = proposal.metadata.get("metadata_hash").unwrap(); + let metadata_url = proposal.metadata.get("metadata_location").unwrap(); + + // Compute the hash and verify accordingly + let mut metadata_verified = false; + let mut actual_metadata_hash = "Unable to fetch metadata url".to_string(); + let mut actual_metadata = None; + if let Ok(url) = Url::parse(metadata_url) { + if let Ok(bytes) = get_metadata_from_url(&url).await { + let hash = HashValue::sha3_256_of(&bytes); + metadata_verified = metadata_hash == &hash.to_hex(); + actual_metadata_hash = hash.to_hex(); + if let Ok(metadata) = String::from_utf8(bytes) { + actual_metadata = Some(metadata); + } + } + } + + Ok(VerifiedProposal { + metadata_verified, + actual_metadata_hash, + actual_metadata, + proposal, + }) + } +} + +/// List the last 100 visible onchain proposals +/// +/// Note, if the full node you are talking to is pruning data, it may not have some of the +/// proposals show here +#[derive(Parser)] +pub struct ListProposals { + #[clap(flatten)] + rest_options: RestOptions, + #[clap(flatten)] + profile: ProfileOptions, +} + +#[async_trait] +impl CliCommand> for ListProposals { + fn command_name(&self) -> &'static str { + "ListProposals" + } + + async fn execute(mut self) -> CliTypedResult> { + // List out known proposals based on events + let client = self.rest_options.client(&self.profile)?; + + let events = client + .get_account_events_bcs( + AccountAddress::ONE, + "0x1::aptos_governance::GovernanceEvents", + "create_proposal_events", + None, + Some(100), + ) + .await? + .into_inner(); + let mut proposals = vec![]; + + for event in &events { + match bcs::from_bytes::(event.event.event_data()) { + Ok(valid_event) => proposals.push(valid_event.into()), + Err(err) => { + eprintln!( + "Event: {:?} cannot be parsed as a proposal: {:?}", + event, err + ) + }, + } + } + + // TODO: Show more information about proposal? + Ok(proposals) + } +} + +/// Verify a proposal given the source code of the script +/// +/// The script's bytecode or source can be provided and it will +/// verify whether the hash matches the onchain hash +#[derive(Parser)] +pub struct VerifyProposal { + /// The id of the onchain proposal + #[clap(long)] + pub(crate) proposal_id: u64, + + #[clap(flatten)] + pub(crate) compile_proposal_args: CompileScriptFunction, + #[clap(flatten)] + pub(crate) rest_options: RestOptions, + #[clap(flatten)] + pub(crate) profile: ProfileOptions, + #[clap(flatten)] + pub(crate) prompt_options: PromptOptions, +} + +#[async_trait] +impl CliCommand for VerifyProposal { + fn command_name(&self) -> &'static str { + "VerifyProposal" + } + + async fn execute(mut self) -> CliTypedResult { + // Compile local first to get the hash + let (_, hash) = self + .compile_proposal_args + .compile("SubmitProposal", self.prompt_options)?; + + // Retrieve the onchain proposal + let client = self.rest_options.client(&self.profile)?; + let forum = client + .get_account_resource_bcs::( + AccountAddress::ONE, + "0x1::voting::VotingForum<0x1::governance_proposal::GovernanceProposal>", + ) + .await? + .into_inner(); + let voting_table = forum.table_handle.0; + + let proposal: Proposal = get_proposal(&client, voting_table, self.proposal_id) + .await? + .into(); + + // Compare the hashes + let computed_hash = hash.to_hex(); + let onchain_hash = proposal.execution_hash; + + Ok(VerifyProposalResponse { + verified: computed_hash == onchain_hash, + computed_hash, + onchain_hash, + }) + } +} + +async fn get_proposal( + client: &aptos_rest_client::Client, + voting_table: AccountAddress, + proposal_id: u64, +) -> CliTypedResult { + let json = client + .get_table_item( + voting_table, + "u64", + "0x1::voting::Proposal<0x1::governance_proposal::GovernanceProposal>", + format!("{}", proposal_id), + ) + .await? + .into_inner(); + serde_json::from_value(json) + .map_err(|err| CliError::CommandArgumentError(format!("Failed to parse proposal {}", err))) +} + +/// Submit a governance proposal +#[derive(Parser)] +pub struct SubmitProposal { + /// Location of the JSON metadata of the proposal + /// + /// If this location does not keep the metadata in the exact format, it will be less likely + /// that voters will approve this proposal, as they won't be able to verify it. + #[clap(long)] + pub(crate) metadata_url: Url, + + #[cfg(feature = "no-upload-proposal")] + /// A JSON file to be uploaded later at the metadata URL + /// + /// If this does not match properly, voters may choose to vote no. For real proposals, + /// it is better to already have it uploaded at the URL. + #[clap(long)] + pub(crate) metadata_path: Option, + + #[clap(long)] + pub(crate) is_multi_step: bool, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, + #[clap(flatten)] + pub(crate) pool_address_args: PoolAddressArgs, + #[clap(flatten)] + pub(crate) compile_proposal_args: CompileScriptFunction, +} + +#[async_trait] +impl CliCommand for SubmitProposal { + fn command_name(&self) -> &'static str { + "SubmitProposal" + } + + async fn execute(mut self) -> CliTypedResult { + let (_bytecode, script_hash) = self + .compile_proposal_args + .compile("SubmitProposal", self.txn_options.prompt_options)?; + + // Validate the proposal metadata + let (metadata, metadata_hash) = self.get_metadata().await?; + + println!( + "{}\n\tMetadata Hash: {}\n\tScript Hash: {}", + metadata, metadata_hash, script_hash + ); + prompt_yes_with_override( + "Do you want to submit this proposal?", + self.txn_options.prompt_options, + )?; + + let txn: Transaction = if self.is_multi_step { + self.txn_options + .submit_transaction(aptos_stdlib::aptos_governance_create_proposal_v2( + self.pool_address_args.pool_address, + script_hash.to_vec(), + self.metadata_url.to_string().as_bytes().to_vec(), + metadata_hash.to_hex().as_bytes().to_vec(), + true, + )) + .await? + } else { + self.txn_options + .submit_transaction(aptos_stdlib::aptos_governance_create_proposal( + self.pool_address_args.pool_address, + script_hash.to_vec(), + self.metadata_url.to_string().as_bytes().to_vec(), + metadata_hash.to_hex().as_bytes().to_vec(), + )) + .await? + }; + let txn_summary = TransactionSummary::from(&txn); + if let Transaction::UserTransaction(inner) = txn { + // Find event with proposal id + let proposal_id = if let Some(event) = inner.events.into_iter().find(|event| { + event.typ.to_string().as_str() == "0x1::aptos_governance::CreateProposalEvent" + }) { + let data: CreateProposalEvent = + serde_json::from_value(event.data).map_err(|_| { + CliError::UnexpectedError( + "Failed to parse Proposal event to get ProposalId".to_string(), + ) + })?; + Some(data.proposal_id.0) + } else { + warn!("No proposal event found to find proposal id"); + None + }; + + return Ok(ProposalSubmissionSummary { + proposal_id, + transaction: txn_summary, + }); + } + Err(CliError::UnexpectedError( + "Unable to find parse proposal transaction output".to_string(), + )) + } +} + +impl SubmitProposal { + /// Retrieve metadata and validate it + async fn get_metadata(&self) -> CliTypedResult<(ProposalMetadata, HashValue)> { + #[cfg(feature = "no-upload-proposal")] + let bytes = if let Some(ref path) = self.metadata_path { + read_from_file(path)? + } else { + get_metadata_from_url(&self.metadata_url).await? + }; + #[cfg(not(feature = "no-upload-proposal"))] + let bytes = get_metadata_from_url(&self.metadata_url).await?; + + let metadata: ProposalMetadata = serde_json::from_slice(&bytes).map_err(|err| { + CliError::CommandArgumentError(format!( + "Metadata is not in a proper JSON format: {}", + err + )) + })?; + Url::parse(&metadata.source_code_url).map_err(|err| { + CliError::CommandArgumentError(format!( + "Source code URL {} is invalid {}", + metadata.source_code_url, err + )) + })?; + Url::parse(&metadata.discussion_url).map_err(|err| { + CliError::CommandArgumentError(format!( + "Discussion URL {} is invalid {}", + metadata.discussion_url, err + )) + })?; + let metadata_hash = HashValue::sha3_256_of(&bytes); + Ok((metadata, metadata_hash)) + } +} + +/// Retrieve the Metadata from the given URL +async fn get_metadata_from_url(metadata_url: &Url) -> CliTypedResult> { + let client = reqwest::ClientBuilder::default() + .tls_built_in_root_certs(true) + .build() + .map_err(|err| CliError::UnexpectedError(format!("Failed to build HTTP client {}", err)))?; + client + .get(metadata_url.clone()) + .send() + .await + .map_err(|err| { + CliError::CommandArgumentError(format!( + "Failed to fetch metadata url {}: {}", + metadata_url, err + )) + })? + .bytes() + .await + .map(|b| b.to_vec()) + .map_err(|err| { + CliError::CommandArgumentError(format!( + "Failed to fetch metadata url {}: {}", + metadata_url, err + )) + }) +} + +#[derive(Debug, Deserialize, Serialize)] +struct CreateProposalEvent { + proposal_id: U64, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct ProposalSubmissionSummary { + proposal_id: Option, + #[serde(flatten)] + transaction: TransactionSummary, +} + +/// Submit a vote on a proposal +/// +/// Votes can only be given on proposals that are currently open for voting. You can vote +/// with `--yes` for a yes vote, and `--no` for a no vote. +#[derive(Parser)] +pub struct SubmitVote { + /// Id of the proposal to vote on + #[clap(long)] + pub(crate) proposal_id: u64, + + /// Vote to accept the proposal + #[clap(long, group = "vote")] + pub(crate) yes: bool, + + /// Vote to reject the proposal + #[clap(long, group = "vote")] + pub(crate) no: bool, + + /// Space separated list of pool addresses. + #[clap(long, multiple_values = true, parse(try_from_str=crate::common::types::load_account_arg))] + pub(crate) pool_addresses: Vec, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand> for SubmitVote { + fn command_name(&self) -> &'static str { + "SubmitVote" + } + + async fn execute(mut self) -> CliTypedResult> { + let (vote_str, vote) = match (self.yes, self.no) { + (true, false) => ("Yes", true), + (false, true) => ("No", false), + (_, _) => { + return Err(CliError::CommandArgumentError( + "Must choose only either --yes or --no".to_string(), + )); + }, + }; + + let client: &Client = &self + .txn_options + .rest_options + .client(&self.txn_options.profile_options)?; + let proposal_id = self.proposal_id; + let voting_records = client + .get_account_resource_bcs::( + CORE_CODE_ADDRESS, + "0x1::aptos_governance::VotingRecords", + ) + .await + .unwrap() + .into_inner() + .votes; + + let mut summaries: Vec = vec![]; + for pool_address in self.pool_addresses { + let voting_record = client + .get_table_item( + voting_records, + "0x1::aptos_governance::RecordKey", + "bool", + VotingRecord { + proposal_id: proposal_id.to_string(), + stake_pool: pool_address, + }, + ) + .await; + let voted = if let Ok(voting_record) = voting_record { + voting_record.into_inner().as_bool().unwrap() + } else { + false + }; + if voted { + println!("Stake pool {} already voted", pool_address); + continue; + } + + let stake_pool = client + .get_account_resource_bcs::(pool_address, "0x1::stake::StakePool") + .await? + .into_inner(); + let voting_power = stake_pool.get_governance_voting_power(); + + prompt_yes_with_override( + &format!( + "Vote {} with voting power = {} from stake pool {}?", + vote_str, voting_power, pool_address + ), + self.txn_options.prompt_options, + )?; + + summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::aptos_governance_vote( + pool_address, + proposal_id, + vote, + )) + .await + .map(TransactionSummary::from)?, + ); + } + Ok(summaries) + } +} + +/// Submit a transaction to approve a proposal's script hash to bypass the transaction size limit. +/// This is needed for upgrading large packages such as aptos-framework. +#[derive(Parser)] +pub struct ApproveExecutionHash { + /// Id of the proposal to vote on + #[clap(long)] + pub(crate) proposal_id: u64, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for ApproveExecutionHash { + fn command_name(&self) -> &'static str { + "ApproveExecutionHash" + } + + async fn execute(mut self) -> CliTypedResult { + Ok(self + .txn_options + .submit_transaction( + aptos_stdlib::aptos_governance_add_approved_script_hash_script(self.proposal_id), + ) + .await + .map(TransactionSummary::from)?) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct VotingRecord { + proposal_id: String, + stake_pool: AccountAddress, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ProposalMetadata { + title: String, + description: String, + source_code_url: String, + discussion_url: String, +} + +impl std::fmt::Display for ProposalMetadata { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Proposal:\n\tTitle:{}\n\tDescription:{}\n\tSource code URL:{}\n\tDiscussion URL:{}", + self.title, self.description, self.source_code_url, self.discussion_url + ) + } +} + +fn compile_in_temp_dir( + script_name: &str, + script_path: &Path, + framework_package_args: &FrameworkPackageArgs, + prompt_options: PromptOptions, + bytecode_version: Option, +) -> CliTypedResult<(Vec, HashValue)> { + // Make a temporary directory for compilation + let temp_dir = TempDir::new().map_err(|err| { + CliError::UnexpectedError(format!("Failed to create temporary directory {}", err)) + })?; + + // Initialize a move directory + let package_dir = temp_dir.path(); + framework_package_args.init_move_dir( + package_dir, + script_name, + BTreeMap::new(), + prompt_options, + )?; + + // Insert the new script + let sources_dir = package_dir.join("sources"); + let new_script_path = if let Some(file_name) = script_path.file_name() { + sources_dir.join(file_name) + } else { + // If for some reason we can't get the move file + sources_dir.join("script.move") + }; + fs::copy(script_path, new_script_path.as_path()).map_err(|err| { + CliError::IO( + format!( + "Failed to copy {} to {}", + script_path.display(), + new_script_path.display() + ), + err, + ) + })?; + + // Compile the script + compile_script( + framework_package_args.skip_fetch_latest_git_deps, + package_dir, + bytecode_version, + ) +} + +fn compile_script( + skip_fetch_latest_git_deps: bool, + package_dir: &Path, + bytecode_version: Option, +) -> CliTypedResult<(Vec, HashValue)> { + let build_options = BuildOptions { + with_srcs: false, + with_abis: false, + with_source_maps: false, + with_error_map: false, + skip_fetch_latest_git_deps, + bytecode_version, + ..BuildOptions::default() + }; + + let pack = BuiltPackage::build(package_dir.to_path_buf(), build_options) + .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?; + + let scripts_count = pack.script_count(); + + if scripts_count != 1 { + return Err(CliError::UnexpectedError(format!( + "Only one script can be prepared a time. Make sure one and only one script file \ + is included in the Move package. Found {} scripts.", + scripts_count + ))); + } + + let bytes = pack.extract_script_code().pop().unwrap(); + let hash = HashValue::sha3_256_of(bytes.as_slice()); + Ok((bytes, hash)) +} + +/// Execute a proposal that has passed voting requirements +#[derive(Parser)] +pub struct ExecuteProposal { + /// Proposal Id being executed + #[clap(long)] + pub(crate) proposal_id: u64, + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, + #[clap(flatten)] + pub(crate) compile_proposal_args: CompileScriptFunction, +} + +#[async_trait] +impl CliCommand for ExecuteProposal { + fn command_name(&self) -> &'static str { + "ExecuteProposal" + } + + async fn execute(mut self) -> CliTypedResult { + let (bytecode, _script_hash) = self + .compile_proposal_args + .compile("ExecuteProposal", self.txn_options.prompt_options)?; + // TODO: Check hash so we don't do a failed roundtrip? + + let args = vec![TransactionArgument::U64(self.proposal_id)]; + let txn = TransactionPayload::Script(Script::new(bytecode, vec![], args)); + + self.txn_options + .submit_transaction(txn) + .await + .map(TransactionSummary::from) + } +} + +/// Compile a specified script. +#[derive(Parser)] +pub struct CompileScriptFunction { + /// Path to the Move script for the proposal + #[clap(long, group = "script", parse(from_os_str))] + pub script_path: Option, + + /// Path to the Move script for the proposal + #[clap(long, group = "script", parse(from_os_str))] + pub compiled_script_path: Option, + + #[clap(flatten)] + pub(crate) framework_package_args: FrameworkPackageArgs, + + #[clap(long)] + pub(crate) bytecode_version: Option, +} + +impl CompileScriptFunction { + pub(crate) fn compile( + &self, + script_name: &str, + prompt_options: PromptOptions, + ) -> CliTypedResult<(Vec, HashValue)> { + if let Some(compiled_script_path) = &self.compiled_script_path { + let bytes = std::fs::read(compiled_script_path).map_err(|e| { + CliError::IO(format!("Unable to read {:?}", self.compiled_script_path), e) + })?; + let hash = HashValue::sha3_256_of(bytes.as_slice()); + return Ok((bytes, hash)); + } + + // Check script file + let script_path = self + .script_path + .as_ref() + .ok_or_else(|| { + CliError::CommandArgumentError( + "Must choose either --compiled-script-path or --script-path".to_string(), + ) + })? + .as_path(); + if !script_path.exists() { + return Err(CliError::CommandArgumentError(format!( + "{} does not exist", + script_path.display() + ))); + } else if script_path.is_dir() { + return Err(CliError::CommandArgumentError(format!( + "{} is a directory", + script_path.display() + ))); + } + + // Compile script + compile_in_temp_dir( + script_name, + script_path, + &self.framework_package_args, + prompt_options, + self.bytecode_version, + ) + } +} + +/// Generates a package upgrade proposal script. +#[derive(Parser)] +pub struct GenerateUpgradeProposal { + /// Address of the account which the proposal addresses. + #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] + pub(crate) account: AccountAddress, + + /// Where to store the generated proposal + #[clap(long, parse(from_os_str), default_value = "proposal.move")] + pub(crate) output: PathBuf, + + /// What artifacts to include in the package. This can be one of `none`, `sparse`, and + /// `all`. `none` is the most compact form and does not allow to reconstruct a source + /// package from chain; `sparse` is the minimal set of artifacts needed to reconstruct + /// a source package; `all` includes all available artifacts. The choice of included + /// artifacts heavily influences the size and therefore gas cost of publishing: `none` + /// is the size of bytecode alone; `sparse` is roughly 2 times as much; and `all` 3-4 + /// as much. + #[clap(long, default_value_t = IncludedArtifacts::Sparse)] + pub(crate) included_artifacts: IncludedArtifacts, + + /// Generate the script for mainnet governance proposal by default or generate the upgrade script for testnet. + #[clap(long)] + pub(crate) testnet: bool, + + #[clap(long, default_value = "")] + pub(crate) next_execution_hash: String, + + #[clap(flatten)] + pub(crate) move_options: MovePackageDir, +} + +#[async_trait] +impl CliCommand<()> for GenerateUpgradeProposal { + fn command_name(&self) -> &'static str { + "GenerateUpgradeProposal" + } + + async fn execute(self) -> CliTypedResult<()> { + let GenerateUpgradeProposal { + move_options, + account, + included_artifacts, + output, + testnet, + next_execution_hash, + } = self; + let package_path = move_options.get_package_path()?; + let options = included_artifacts.build_options( + move_options.skip_fetch_latest_git_deps, + move_options.named_addresses(), + move_options.bytecode_version, + ); + let package = BuiltPackage::build(package_path, options)?; + let release = ReleasePackage::new(package)?; + + // If we're generating a single-step proposal on testnet + if testnet && next_execution_hash.is_empty() { + release.generate_script_proposal_testnet(account, output)?; + // If we're generating a single-step proposal on mainnet + } else if next_execution_hash.is_empty() { + release.generate_script_proposal(account, output)?; + // If we're generating a multi-step proposal + } else { + let next_execution_hash_bytes = hex::decode(next_execution_hash)?; + release.generate_script_proposal_multi_step( + account, + output, + next_execution_hash_bytes, + )?; + }; + Ok(()) + } +} + +/// Generate execution hash for a specified script. +#[derive(Parser)] +pub struct GenerateExecutionHash { + #[clap(long)] + pub script_path: Option, +} + +impl GenerateExecutionHash { + pub fn generate_hash(&self) -> CliTypedResult<(Vec, HashValue)> { + CompileScriptFunction { + script_path: self.script_path.clone(), + compiled_script_path: None, + framework_package_args: FrameworkPackageArgs { + framework_git_rev: None, + framework_local_dir: Option::from({ + let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.pop(); + path.pop(); + path.join("aptos-move") + .join("framework") + .join("aptos-framework") + .canonicalize() + .map_err(|err| { + CliError::IO( + format!("Failed to canonicalize aptos framework path: {:?}", path), + err, + ) + })? + }), + skip_fetch_latest_git_deps: false, + }, + bytecode_version: None, + } + .compile("execution_hash", PromptOptions::yes()) + } +} + +/// Response for `verify proposal` +#[derive(Serialize, Deserialize, Debug)] +pub struct VerifyProposalResponse { + pub verified: bool, + pub computed_hash: String, + pub onchain_hash: String, +} + +/// Voting forum onchain type +/// +/// TODO: Move to a shared location +#[derive(Serialize, Deserialize, Debug)] +pub struct VotingForum { + table_handle: TableHandle, + events: VotingEvents, + next_proposal_id: u64, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct VotingEvents { + create_proposal_events: EventHandle, + register_forum_events: EventHandle, + resolve_proposal_events: EventHandle, + vote_events: EventHandle, +} + +/// Summary of proposal from the listing events for `ListProposals` +#[derive(Serialize, Deserialize, Debug)] +struct ProposalSummary { + proposer: AccountAddress, + stake_pool: AccountAddress, + proposal_id: u64, + execution_hash: String, + proposal_metadata: BTreeMap, +} + +impl From for ProposalSummary { + fn from(event: CreateProposalFullEvent) -> Self { + let proposal_metadata = event + .proposal_metadata + .into_iter() + .map(|(key, value)| (key, String::from_utf8(value).unwrap())) + .collect(); + ProposalSummary { + proposer: event.proposer, + stake_pool: event.stake_pool, + proposal_id: event.proposal_id, + execution_hash: hex::encode(event.execution_hash), + proposal_metadata, + } + } +} + +#[derive(Deserialize)] +struct CreateProposalFullEvent { + proposer: AccountAddress, + stake_pool: AccountAddress, + proposal_id: u64, + execution_hash: Vec, + proposal_metadata: Vec<(String, Vec)>, +} + +/// A proposal and the verified information about it +#[derive(Serialize, Deserialize, Debug)] +pub struct VerifiedProposal { + metadata_verified: bool, + actual_metadata_hash: String, + actual_metadata: Option, + proposal: Proposal, +} + +/// A reformatted type that has human readable version of the proposal onchain +#[derive(Serialize, Deserialize, Debug)] +pub struct Proposal { + proposer: AccountAddress, + metadata: BTreeMap, + creation_time_secs: u64, + execution_hash: String, + min_vote_threshold: u128, + expiration_secs: u64, + early_resolution_vote_threshold: Option, + yes_votes: u128, + no_votes: u128, + is_resolved: bool, + resolution_time_secs: u64, +} + +impl From for Proposal { + fn from(proposal: JsonProposal) -> Self { + let metadata = proposal + .metadata + .data + .into_iter() + .map(|pair| { + let value = match pair.key.as_str() { + "metadata_hash" => String::from_utf8(pair.value.0) + .unwrap_or_else(|_| "Failed to parse utf8".to_string()), + "metadata_location" => String::from_utf8(pair.value.0) + .unwrap_or_else(|_| "Failed to parse utf8".to_string()), + "RESOLVABLE_TIME_METADATA_KEY" => bcs::from_bytes::(pair.value.inner()) + .map(|inner| inner.to_string()) + .unwrap_or_else(|_| "Failed to parse u64".to_string()), + _ => pair.value.to_string(), + }; + (pair.key, value) + }) + .collect(); + + Proposal { + proposer: proposal.proposer.into(), + metadata, + creation_time_secs: proposal.creation_time_secs.into(), + execution_hash: format!("{:x}", proposal.execution_hash), + min_vote_threshold: proposal.min_vote_threshold.into(), + expiration_secs: proposal.expiration_secs.into(), + early_resolution_vote_threshold: proposal + .early_resolution_vote_threshold + .vec + .first() + .map(|inner| inner.0), + yes_votes: proposal.yes_votes.into(), + no_votes: proposal.no_votes.into(), + is_resolved: proposal.is_resolved, + resolution_time_secs: proposal.resolution_time_secs.into(), + } + } +} + +/// An ugly JSON parsing version for from the JSON API +#[derive(Serialize, Deserialize, Debug)] +struct JsonProposal { + creation_time_secs: U64, + early_resolution_vote_threshold: JsonEarlyResolutionThreshold, + execution_hash: aptos_rest_client::aptos_api_types::HashValue, + expiration_secs: U64, + is_resolved: bool, + min_vote_threshold: U128, + no_votes: U128, + resolution_time_secs: U64, + yes_votes: U128, + proposer: Address, + metadata: JsonMetadata, +} + +#[derive(Serialize, Deserialize, Debug)] +struct JsonEarlyResolutionThreshold { + vec: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +struct JsonMetadata { + data: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +struct JsonMetadataPair { + key: String, + value: HexEncodedBytes, +} diff --git a/movement-sdk/clis/aptos/src/lib.rs b/movement-sdk/clis/aptos/src/lib.rs new file mode 100644 index 000000000..b8475e281 --- /dev/null +++ b/movement-sdk/clis/aptos/src/lib.rs @@ -0,0 +1,94 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#![deny(unsafe_code)] + +pub mod account; +pub mod common; +pub mod config; +pub mod ffi; +pub mod genesis; +pub mod governance; +pub mod move_tool; +pub mod node; +pub mod op; +pub mod stake; +#[cfg(any(test, feature = "fuzzing"))] +pub mod test; +pub mod update; +pub mod faucet; + +use crate::common::{ + types::{CliCommand, CliResult, CliTypedResult}, + utils::cli_build_information, +}; +use async_trait::async_trait; +use clap::Parser; +use std::collections::BTreeMap; + +/// Command Line Interface (CLI) for developing and interacting with the Aptos blockchain +#[derive(Parser)] +#[clap(name = "aptos", author, version, propagate_version = true)] +pub enum Tool { + #[clap(subcommand)] + Account(account::AccountTool), + #[clap(subcommand)] + Config(config::ConfigTool), + #[clap(subcommand)] + Genesis(genesis::GenesisTool), + #[clap(subcommand)] + Governance(governance::GovernanceTool), + Info(InfoTool), + Init(common::init::InitTool), + #[clap(subcommand)] + Key(op::key::KeyTool), + #[clap(subcommand)] + Move(move_tool::MoveTool), + #[clap(subcommand)] + Multisig(account::MultisigAccountTool), + #[clap(subcommand)] + Node(node::NodeTool), + #[clap(subcommand)] + Stake(stake::StakeTool), + Update(update::UpdateTool), + Faucet(faucet::FaucetTool), +} + +impl Tool { + pub async fn execute(self) -> CliResult { + use Tool::*; + match self { + Account(tool) => tool.execute().await, + Config(tool) => tool.execute().await, + Genesis(tool) => tool.execute().await, + Governance(tool) => tool.execute().await, + Info(tool) => tool.execute_serialized().await, + // TODO: Replace entirely with config init + Init(tool) => tool.execute_serialized_success().await, + Key(tool) => tool.execute().await, + Move(tool) => tool.execute().await, + Multisig(tool) => tool.execute().await, + Node(tool) => tool.execute().await, + Stake(tool) => tool.execute().await, + Update(tool) => tool.execute_serialized().await, + Faucet(tool) => tool.execute_serialized().await, + } + } +} + +/// Show build information about the CLI +/// +/// This is useful for debugging as well as determining what versions are compatible with the CLI +#[derive(Parser)] +pub struct InfoTool {} + +#[async_trait] +impl CliCommand> for InfoTool { + fn command_name(&self) -> &'static str { + "GetCLIInfo" + } + + async fn execute(self) -> CliTypedResult> { + Ok(cli_build_information()) + } +} diff --git a/movement-sdk/clis/aptos/src/main.rs b/movement-sdk/clis/aptos/src/main.rs new file mode 100644 index 000000000..4fff4f71d --- /dev/null +++ b/movement-sdk/clis/aptos/src/main.rs @@ -0,0 +1,31 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! Aptos is a one stop tool for operations, debugging, and other operations with the blockchain + +#![forbid(unsafe_code)] + +#[cfg(unix)] +#[global_allocator] +static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; + +pub use movement::{move_tool, Tool}; +use clap::Parser; +use std::process::exit; + +#[tokio::main] +async fn main() { + // Register hooks + move_tool::register_package_hooks(); + // Run the corresponding tools + let result = Tool::parse().execute().await; + + // At this point, we'll want to print and determine whether to exit for an error code + match result { + Ok(inner) => println!("{}", inner), + Err(inner) => { + println!("{}", inner); + exit(1); + }, + } +} diff --git a/movement-sdk/clis/aptos/src/move_tool/aptos_debug_natives.rs b/movement-sdk/clis/aptos/src/move_tool/aptos_debug_natives.rs new file mode 100644 index 000000000..7abcb46f7 --- /dev/null +++ b/movement-sdk/clis/aptos/src/move_tool/aptos_debug_natives.rs @@ -0,0 +1,26 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use aptos_gas::{AbstractValueSizeGasParameters, NativeGasParameters, LATEST_GAS_FEATURE_VERSION}; +use aptos_types::on_chain_config::{Features, TimedFeatures}; +use aptos_vm::natives; +use move_vm_runtime::native_functions::NativeFunctionTable; +use std::sync::Arc; + +// move_stdlib has the testing feature enabled to include debug native functions +pub fn aptos_debug_natives( + gas_parameters: NativeGasParameters, + abs_val_size_gas_params: AbstractValueSizeGasParameters, +) -> NativeFunctionTable { + // As a side effect, also configure for unit testing + natives::configure_for_unit_test(); + // Return all natives -- build with the 'testing' feature, therefore containing + // debug related functions. + natives::aptos_natives( + gas_parameters, + abs_val_size_gas_params, + LATEST_GAS_FEATURE_VERSION, + TimedFeatures::enable_all(), + Arc::new(Features::default()), + ) +} diff --git a/movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/README.md b/movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/README.md new file mode 100644 index 000000000..abfa0f995 --- /dev/null +++ b/movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/README.md @@ -0,0 +1,24 @@ +This is a small example of using the new `aptos` dependency. This shall be removed once we have +documentation/tests. + +`pack2` contains a package which is used by `pack1` as follows: + +``` +[dependencies] +Pack2 = { aptos = "http://localhost:8080", address = "default" } +``` + +To see it working: + +```shell +# Start a node with an account +aptos node run-local-testnet --with-faucet & +aptos account create --account default --use-faucet +# Compile and publish pack2 +cd pack2 +aptos move compile --named-addresses project=default +aptos move publish --named-addresses project=default +# Compile pack1 agains the published pack2 +cd ../pack1 +aptos move compile --named-addresses project=default +``` \ No newline at end of file diff --git a/movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/pack1/Move.toml b/movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/pack1/Move.toml new file mode 100644 index 000000000..dab5d3fe7 --- /dev/null +++ b/movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/pack1/Move.toml @@ -0,0 +1,6 @@ +[package] +name = "Pack1" +version = "0.0.0" + +[dependencies] +Pack2 = { aptos = "http://localhost:8080", address = "default" } diff --git a/movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/pack1/sources/hello.move b/movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/pack1/sources/hello.move new file mode 100644 index 000000000..fe091952d --- /dev/null +++ b/movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/pack1/sources/hello.move @@ -0,0 +1,7 @@ +module project::test { + use project::m; + + public entry fun test(_sender: &signer) { + assert!(m::add(1, 2) == 1 + 2, 1); + } +} diff --git a/movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/pack2/Move.toml b/movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/pack2/Move.toml new file mode 100644 index 000000000..c5e6c7d00 --- /dev/null +++ b/movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/pack2/Move.toml @@ -0,0 +1,3 @@ +[package] +name = "Pack2" +version = "0.0.0" diff --git a/movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/pack2/sources/m.move b/movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/pack2/sources/m.move new file mode 100644 index 000000000..ade22fadb --- /dev/null +++ b/movement-sdk/clis/aptos/src/move_tool/aptos_dep_example/pack2/sources/m.move @@ -0,0 +1,3 @@ +module project::m { + public fun add(x: u64, y: u64): u64 { x + y } +} diff --git a/movement-sdk/clis/aptos/src/move_tool/coverage.rs b/movement-sdk/clis/aptos/src/move_tool/coverage.rs new file mode 100644 index 000000000..78ffdb021 --- /dev/null +++ b/movement-sdk/clis/aptos/src/move_tool/coverage.rs @@ -0,0 +1,184 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::types::{CliCommand, CliError, CliResult, CliTypedResult, MovePackageDir}; +use async_trait::async_trait; +use clap::{Parser, Subcommand}; +use move_compiler::compiled_unit::{CompiledUnit, NamedCompiledModule}; +use move_coverage::{ + coverage_map::CoverageMap, format_csv_summary, format_human_summary, + source_coverage::SourceCoverageBuilder, summary::summarize_inst_cov, +}; +use move_disassembler::disassembler::Disassembler; +use move_package::{compilation::compiled_package::CompiledPackage, BuildConfig}; + +/// Display a coverage summary for all modules in a package +/// +#[derive(Debug, Parser)] +pub struct SummaryCoverage { + /// Display function coverage summaries + /// + /// When provided, it will include coverage on a function level + #[clap(long)] + pub summarize_functions: bool, + /// Output CSV data of coverage + #[clap(long = "csv")] + pub output_csv: bool, + /// A filter string to determine which unit tests to compute coverage on + #[clap(long, short)] + pub filter: Option, + #[clap(flatten)] + pub move_options: MovePackageDir, +} + +impl SummaryCoverage { + pub fn coverage(self) -> CliTypedResult<()> { + let (coverage_map, package) = compile_coverage(self.move_options)?; + let modules: Vec<_> = package + .root_modules() + .filter_map(|unit| { + let mut retain = true; + if let Some(filter_str) = &self.filter { + if !&unit.unit.name().as_str().contains(filter_str.as_str()) { + retain = false; + } + } + match &unit.unit { + CompiledUnit::Module(NamedCompiledModule { module, .. }) if retain => { + Some(module.clone()) + }, + _ => None, + } + }) + .collect(); + let coverage_map = coverage_map.to_unified_exec_map(); + if self.output_csv { + format_csv_summary( + modules.as_slice(), + &coverage_map, + summarize_inst_cov, + &mut std::io::stdout(), + ) + } else { + format_human_summary( + modules.as_slice(), + &coverage_map, + summarize_inst_cov, + &mut std::io::stdout(), + self.summarize_functions, + ) + } + Ok(()) + } +} + +#[async_trait] +impl CliCommand<()> for SummaryCoverage { + fn command_name(&self) -> &'static str { + "SummaryCoverage" + } + + async fn execute(self) -> CliTypedResult<()> { + self.coverage() + } +} + +/// Display coverage information about the module against source code +#[derive(Debug, Parser)] +pub struct SourceCoverage { + #[clap(long = "module")] + pub module_name: String, + #[clap(flatten)] + pub move_options: MovePackageDir, +} + +#[async_trait] +impl CliCommand<()> for SourceCoverage { + fn command_name(&self) -> &'static str { + "SourceCoverage" + } + + async fn execute(self) -> CliTypedResult<()> { + let (coverage_map, package) = compile_coverage(self.move_options)?; + let unit = package.get_module_by_name_from_root(&self.module_name)?; + let source_path = &unit.source_path; + let (module, source_map) = match &unit.unit { + CompiledUnit::Module(NamedCompiledModule { + module, source_map, .. + }) => (module, source_map), + _ => panic!("Should all be modules"), + }; + let source_coverage = SourceCoverageBuilder::new(module, &coverage_map, source_map); + source_coverage + .compute_source_coverage(source_path) + .output_source_coverage(&mut std::io::stdout()) + .map_err(|err| CliError::UnexpectedError(format!("Failed to get coverage {}", err))) + } +} + +/// Display coverage information about the module against disassembled bytecode +#[derive(Debug, Parser)] +pub struct BytecodeCoverage { + #[clap(long = "module")] + pub module_name: String, + #[clap(flatten)] + pub move_options: MovePackageDir, +} + +#[async_trait] +impl CliCommand<()> for BytecodeCoverage { + fn command_name(&self) -> &'static str { + "BytecodeCoverage" + } + + async fn execute(self) -> CliTypedResult<()> { + let (coverage_map, package) = compile_coverage(self.move_options)?; + let unit = package.get_module_by_name_from_root(&self.module_name)?; + let mut disassembler = Disassembler::from_unit(&unit.unit); + disassembler.add_coverage_map(coverage_map.to_unified_exec_map()); + println!("{}", disassembler.disassemble()?); + Ok(()) + } +} + +fn compile_coverage( + move_options: MovePackageDir, +) -> CliTypedResult<(CoverageMap, CompiledPackage)> { + let config = BuildConfig { + additional_named_addresses: move_options.named_addresses(), + test_mode: false, + install_dir: move_options.output_dir.clone(), + ..Default::default() + }; + let path = move_options.get_package_path()?; + let coverage_map = + CoverageMap::from_binary_file(path.join(".coverage_map.mvcov")).map_err(|err| { + CliError::UnexpectedError(format!("Failed to retrieve coverage map {}", err)) + })?; + let package = config + .compile_package(path.as_path(), &mut Vec::new()) + .map_err(|err| CliError::MoveCompilationError(err.to_string()))?; + + Ok((coverage_map, package)) +} + +/// Computes coverage for a package +/// +/// Computes coverage on a previous unit test run for a package. Coverage input must +/// first be built with `aptos move test --coverage` +#[derive(Subcommand)] +pub enum CoveragePackage { + Summary(SummaryCoverage), + Source(SourceCoverage), + Bytecode(BytecodeCoverage), +} + +impl CoveragePackage { + pub async fn execute(self) -> CliResult { + match self { + Self::Summary(tool) => tool.execute_serialized_success().await, + Self::Source(tool) => tool.execute_serialized_success().await, + Self::Bytecode(tool) => tool.execute_serialized_success().await, + } + } +} diff --git a/movement-sdk/clis/aptos/src/move_tool/disassembler.rs b/movement-sdk/clis/aptos/src/move_tool/disassembler.rs new file mode 100644 index 000000000..12e7fa625 --- /dev/null +++ b/movement-sdk/clis/aptos/src/move_tool/disassembler.rs @@ -0,0 +1,148 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::{ + types::{CliCommand, CliError, CliTypedResult, PromptOptions}, + utils::{ + check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, read_from_file, + write_to_user_only_file, + }, +}; +use anyhow::Context; +use async_trait::async_trait; +use clap::Parser; +use move_binary_format::{ + binary_views::BinaryIndexedView, file_format::CompiledScript, CompiledModule, +}; +use move_bytecode_source_map::{mapping::SourceMapping, utils::source_map_from_file}; +use move_command_line_common::files::{ + MOVE_COMPILED_EXTENSION, MOVE_EXTENSION, SOURCE_MAP_EXTENSION, +}; +use move_coverage::coverage_map::CoverageMap; +use move_disassembler::disassembler::{Disassembler, DisassemblerOptions}; +use move_ir_types::location::Spanned; +use std::{fs, path::PathBuf}; + +const DISASSEMBLED_CODE_FILE: &str = "disassembled-code.yaml"; + +/// Disassemble the Move bytecode pointed to +/// +/// For example, if you want to disassemble on chain module: +/// 1. Download the package - aptos move download +/// 2. Compile the package - aptos move compile +/// 3. Cd to package and disassemble - aptos move disassemble --bytecode-path ./test.mv +#[derive(Debug, Parser)] +pub struct Disassemble { + /// Treat input file as a script (default is to treat file as a module) + #[clap(long)] + pub is_script: bool, + + /// The path to the bytecode file to disassemble; + /// + /// let's call it file.mv. We assume that two other files reside under the same directory: + /// a source map file.mvsm (possibly) and the Move source code file.move. + #[clap(long)] + pub bytecode_path: PathBuf, + + /// (Optional) Path to a coverage file for the VM in order to print trace information in the + /// disassembled output. + #[clap(long)] + pub code_coverage_path: Option, + + /// Output directory for the key files + #[clap(long, parse(from_os_str))] + pub(crate) output_dir: Option, + + #[clap(flatten)] + pub(crate) prompt_options: PromptOptions, +} + +#[async_trait] +impl CliCommand for Disassemble { + fn command_name(&self) -> &'static str { + "Disassemble" + } + + async fn execute(self) -> CliTypedResult { + let bytecode_path = self.bytecode_path.as_path(); + let extension = bytecode_path + .extension() + .context("Missing file extension for bytecode file")?; + if extension != MOVE_COMPILED_EXTENSION { + return Err(CliError::UnexpectedError(format!( + "Bad source file extension {:?}; expected {}", + extension, MOVE_COMPILED_EXTENSION + ))); + } + + let bytecode_bytes = read_from_file(bytecode_path)?; + let move_path = bytecode_path.with_extension(MOVE_EXTENSION); + let source_map_path = bytecode_path.with_extension(SOURCE_MAP_EXTENSION); + + let source = fs::read_to_string(move_path).ok(); + let source_map = source_map_from_file(&source_map_path); + + let disassembler_options = DisassemblerOptions { + print_code: true, + only_externally_visible: false, + print_basic_blocks: true, + print_locals: true, + }; + + let no_loc = Spanned::unsafe_no_loc(()).loc; + let module: CompiledModule; + let script: CompiledScript; + let bytecode = if self.is_script { + script = CompiledScript::deserialize(&bytecode_bytes) + .context("Script blob can't be deserialized")?; + BinaryIndexedView::Script(&script) + } else { + module = CompiledModule::deserialize(&bytecode_bytes) + .context("Module blob can't be deserialized")?; + BinaryIndexedView::Module(&module) + }; + + let mut source_mapping = if let Ok(s) = source_map { + SourceMapping::new(s, bytecode) + } else { + SourceMapping::new_from_view(bytecode, no_loc) + .context("Unable to build dummy source mapping")? + }; + + if let Some(source_code) = source { + source_mapping.with_source_code((bytecode_path.display().to_string(), source_code)); + } + + let mut disassembler = Disassembler::new(source_mapping, disassembler_options); + + if let Some(file_path) = &self.code_coverage_path { + disassembler.add_coverage_map( + CoverageMap::from_binary_file(file_path) + .map_err(|_err| { + CliError::UnexpectedError("Unable to read from file_path".to_string()) + })? + .to_unified_exec_map(), + ); + } + + let disassemble_string = disassembler + .disassemble() + .map_err(|_err| CliError::UnexpectedError("Unable to disassemble".to_string()))?; + + let output_dir = dir_default_to_current(self.output_dir.clone())?; + let disassemble_file = output_dir.join(DISASSEMBLED_CODE_FILE); + check_if_file_exists(disassemble_file.as_path(), self.prompt_options)?; + + // Create the directory if it doesn't exist + create_dir_if_not_exist(output_dir.as_path())?; + + // write to file + write_to_user_only_file( + disassemble_file.as_path(), + DISASSEMBLED_CODE_FILE, + disassemble_string.as_bytes(), + )?; + + Ok(disassemble_file.as_path().display().to_string()) + } +} diff --git a/movement-sdk/clis/aptos/src/move_tool/manifest.rs b/movement-sdk/clis/aptos/src/move_tool/manifest.rs new file mode 100644 index 000000000..dbdcbc3e4 --- /dev/null +++ b/movement-sdk/clis/aptos/src/move_tool/manifest.rs @@ -0,0 +1,90 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::types::load_manifest_account_arg; +use aptos_types::account_address::AccountAddress; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::collections::BTreeMap; + +/// A Rust representation of the Move package manifest +/// +/// Note: The original Move package manifest object used by the package system +/// can't be serialized because it uses a symbol mapping +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MovePackageManifest { + pub package: PackageInfo, + #[serde(skip_serializing_if = "BTreeMap::is_empty")] + pub addresses: BTreeMap, + #[serde(skip_serializing_if = "BTreeMap::is_empty")] + pub dependencies: BTreeMap, +} + +/// Representation of an option address so we can print it as "_" +#[derive(Debug, Clone)] +pub struct ManifestNamedAddress { + pub address: Option, +} + +impl From> for ManifestNamedAddress { + fn from(opt: Option) -> Self { + ManifestNamedAddress { address: opt } + } +} + +impl From for Option { + fn from(addr: ManifestNamedAddress) -> Self { + addr.address + } +} + +impl Serialize for ManifestNamedAddress { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if let Some(address) = self.address { + serializer.serialize_str(&address.to_hex_literal()) + } else { + serializer.serialize_str("_") + } + } +} + +impl<'de> Deserialize<'de> for ManifestNamedAddress { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let str = ::deserialize(deserializer)?; + Ok(ManifestNamedAddress { + // TODO: Cleanup unwrap + address: load_manifest_account_arg(&str).unwrap(), + }) + } +} + +/// A Rust representation of a move dependency +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Dependency { + #[serde(skip_serializing_if = "Option::is_none")] + pub local: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub git: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub rev: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub subdir: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub aptos: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub address: Option, +} + +/// A Rust representation of the package info +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PackageInfo { + pub name: String, + pub version: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub author: Option, +} diff --git a/movement-sdk/clis/aptos/src/move_tool/mod.rs b/movement-sdk/clis/aptos/src/move_tool/mod.rs new file mode 100644 index 000000000..560d0f18a --- /dev/null +++ b/movement-sdk/clis/aptos/src/move_tool/mod.rs @@ -0,0 +1,1606 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +mod aptos_debug_natives; +pub mod coverage; +mod disassembler; +mod manifest; +pub mod package_hooks; +mod show; +pub mod stored_package; +mod transactional_tests_runner; + +use crate::{ + account::derive_resource_account::ResourceAccountSeed, + common::{ + types::{ + load_account_arg, ArgWithTypeVec, CliConfig, CliError, CliTypedResult, + ConfigSearchMode, EntryFunctionArguments, MoveManifestAccountWrapper, MovePackageDir, + ProfileOptions, PromptOptions, RestOptions, TransactionOptions, TransactionSummary, + }, + utils::{ + check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, + profile_or_submit, prompt_yes_with_override, write_to_file, + }, + }, + governance::CompileScriptFunction, + move_tool::{ + coverage::SummaryCoverage, + disassembler::Disassemble, + manifest::{Dependency, ManifestNamedAddress, MovePackageManifest, PackageInfo}, + }, + CliCommand, CliResult, +}; +use aptos_crypto::HashValue; +use aptos_framework::{ + build_model, docgen::DocgenOptions, extended_checks, natives::code::UpgradePolicy, + prover::ProverOptions, BuildOptions, BuiltPackage, +}; +use aptos_gas::{AbstractValueSizeGasParameters, NativeGasParameters}; +use aptos_rest_client::aptos_api_types::{EntryFunctionId, MoveType, ViewRequest}; +use aptos_transactional_test_harness::run_aptos_test; +use aptos_types::{ + account_address::{create_resource_address, AccountAddress}, + transaction::{Script, TransactionArgument, TransactionPayload}, +}; +use async_trait::async_trait; +use clap::{ArgEnum, Parser, Subcommand}; +use codespan_reporting::{ + diagnostic::Severity, + term::termcolor::{ColorChoice, StandardStream}, +}; +use itertools::Itertools; +use move_cli::{self, base::test::UnitTestResult}; +use move_command_line_common::env::MOVE_HOME; +use move_core_types::{ + identifier::Identifier, + language_storage::{ModuleId, TypeTag}, + u256::U256, +}; +use move_package::{source_package::layout::SourcePackageLayout, BuildConfig}; +use move_unit_test::UnitTestingConfig; +pub use package_hooks::*; +use serde::{Deserialize, Serialize}; +use std::{ + collections::BTreeMap, + convert::TryFrom, + fmt::{Display, Formatter}, + path::{Path, PathBuf}, + str::FromStr, +}; +pub use stored_package::*; +use tokio::task; +use transactional_tests_runner::TransactionalTestOpts; + +/// Tool for Move related operations +/// +/// This tool lets you compile, test, and publish Move code, in addition +/// to run any other tools that help run, verify, or provide information +/// about this code. +#[derive(Subcommand)] +pub enum MoveTool { + Clean(CleanPackage), + Compile(CompilePackage), + CompileScript(CompileScript), + #[clap(subcommand)] + Coverage(coverage::CoveragePackage), + CreateResourceAccountAndPublishPackage(CreateResourceAccountAndPublishPackage), + Disassemble(Disassemble), + Document(DocumentPackage), + Download(DownloadPackage), + Init(InitPackage), + List(ListPackage), + Prove(ProvePackage), + Publish(PublishPackage), + Run(RunFunction), + RunScript(RunScript), + #[clap(subcommand, hide = true)] + Show(show::ShowTool), + Test(TestPackage), + TransactionalTest(TransactionalTestOpts), + VerifyPackage(VerifyPackage), + View(ViewFunction), +} + +impl MoveTool { + pub async fn execute(self) -> CliResult { + match self { + MoveTool::Clean(tool) => tool.execute_serialized().await, + MoveTool::Compile(tool) => tool.execute_serialized().await, + MoveTool::CompileScript(tool) => tool.execute_serialized().await, + MoveTool::Coverage(tool) => tool.execute().await, + MoveTool::CreateResourceAccountAndPublishPackage(tool) => { + tool.execute_serialized_success().await + }, + MoveTool::Disassemble(tool) => tool.execute_serialized().await, + MoveTool::Document(tool) => tool.execute_serialized().await, + MoveTool::Download(tool) => tool.execute_serialized().await, + MoveTool::Init(tool) => tool.execute_serialized_success().await, + MoveTool::List(tool) => tool.execute_serialized().await, + MoveTool::Prove(tool) => tool.execute_serialized().await, + MoveTool::Publish(tool) => tool.execute_serialized().await, + MoveTool::Run(tool) => tool.execute_serialized().await, + MoveTool::RunScript(tool) => tool.execute_serialized().await, + MoveTool::Show(tool) => tool.execute_serialized().await, + MoveTool::Test(tool) => tool.execute_serialized().await, + MoveTool::TransactionalTest(tool) => tool.execute_serialized_success().await, + MoveTool::VerifyPackage(tool) => tool.execute_serialized().await, + MoveTool::View(tool) => tool.execute_serialized().await, + } + } +} + +#[derive(Parser)] +pub struct FrameworkPackageArgs { + /// Git revision or branch for the Aptos framework + /// + /// This is mutually exclusive with `--framework-local-dir` + #[clap(long, group = "framework_package_args")] + pub(crate) framework_git_rev: Option, + + /// Local framework directory for the Aptos framework + /// + /// This is mutually exclusive with `--framework-git-rev` + #[clap(long, parse(from_os_str), group = "framework_package_args")] + pub(crate) framework_local_dir: Option, + + /// Skip pulling the latest git dependencies + /// + /// If you don't have a network connection, the compiler may fail due + /// to no ability to pull git dependencies. This will allow overriding + /// this for local development. + #[clap(long)] + pub(crate) skip_fetch_latest_git_deps: bool, +} + +impl FrameworkPackageArgs { + pub fn init_move_dir( + &self, + package_dir: &Path, + name: &str, + addresses: BTreeMap, + prompt_options: PromptOptions, + ) -> CliTypedResult<()> { + const APTOS_FRAMEWORK: &str = "AptosFramework"; + const APTOS_GIT_PATH: &str = "https://github.com/aptos-labs/aptos-core.git"; + const SUBDIR_PATH: &str = "aptos-move/framework/aptos-framework"; + const DEFAULT_BRANCH: &str = "main"; + + let move_toml = package_dir.join(SourcePackageLayout::Manifest.path()); + check_if_file_exists(move_toml.as_path(), prompt_options)?; + create_dir_if_not_exist( + package_dir + .join(SourcePackageLayout::Sources.path()) + .as_path(), + )?; + + // Add the framework dependency if it's provided + let mut dependencies = BTreeMap::new(); + if let Some(ref path) = self.framework_local_dir { + dependencies.insert(APTOS_FRAMEWORK.to_string(), Dependency { + local: Some(path.display().to_string()), + git: None, + rev: None, + subdir: None, + aptos: None, + address: None, + }); + } else { + let git_rev = self.framework_git_rev.as_deref().unwrap_or(DEFAULT_BRANCH); + dependencies.insert(APTOS_FRAMEWORK.to_string(), Dependency { + local: None, + git: Some(APTOS_GIT_PATH.to_string()), + rev: Some(git_rev.to_string()), + subdir: Some(SUBDIR_PATH.to_string()), + aptos: None, + address: None, + }); + } + + let manifest = MovePackageManifest { + package: PackageInfo { + name: name.to_string(), + version: "1.0.0".to_string(), + author: None, + }, + addresses, + dependencies, + }; + + write_to_file( + move_toml.as_path(), + SourcePackageLayout::Manifest.location_str(), + toml::to_string_pretty(&manifest) + .map_err(|err| CliError::UnexpectedError(err.to_string()))? + .as_bytes(), + ) + } +} + +/// Creates a new Move package at the given location +/// +/// This will create a directory for a Move package and a corresponding +/// `Move.toml` file. +#[derive(Parser)] +pub struct InitPackage { + /// Name of the new Move package + #[clap(long)] + pub(crate) name: String, + + /// Directory to create the new Move package + #[clap(long, parse(from_os_str))] + pub(crate) package_dir: Option, + + /// Named addresses for the move binary + /// + /// Allows for an address to be put into the Move.toml, or a placeholder `_` + /// + /// Example: alice=0x1234,bob=0x5678,greg=_ + /// + /// Note: This will fail if there are duplicates in the Move.toml file remove those first. + #[clap(long, parse(try_from_str = crate::common::utils::parse_map), default_value = "")] + pub(crate) named_addresses: BTreeMap, + + #[clap(flatten)] + pub(crate) prompt_options: PromptOptions, + + #[clap(flatten)] + pub(crate) framework_package_args: FrameworkPackageArgs, +} + +#[async_trait] +impl CliCommand<()> for InitPackage { + fn command_name(&self) -> &'static str { + "InitPackage" + } + + async fn execute(self) -> CliTypedResult<()> { + let package_dir = dir_default_to_current(self.package_dir.clone())?; + let addresses = self + .named_addresses + .into_iter() + .map(|(key, value)| (key, value.account_address.into())) + .collect(); + + self.framework_package_args.init_move_dir( + package_dir.as_path(), + &self.name, + addresses, + self.prompt_options, + ) + } +} + +/// Compiles a package and returns the associated ModuleIds +#[derive(Parser)] +pub struct CompilePackage { + /// Save the package metadata in the package's build directory + /// + /// If set, package metadata should be generated and stored in the package's build directory. + /// This metadata can be used to construct a transaction to publish a package. + #[clap(long)] + pub(crate) save_metadata: bool, + + #[clap(flatten)] + pub(crate) included_artifacts_args: IncludedArtifactsArgs, + #[clap(flatten)] + pub(crate) move_options: MovePackageDir, +} + +#[async_trait] +impl CliCommand> for CompilePackage { + fn command_name(&self) -> &'static str { + "CompilePackage" + } + + async fn execute(self) -> CliTypedResult> { + let build_options = BuildOptions { + install_dir: self.move_options.output_dir.clone(), + ..self + .included_artifacts_args + .included_artifacts + .build_options( + self.move_options.skip_fetch_latest_git_deps, + self.move_options.named_addresses(), + self.move_options.bytecode_version, + ) + }; + let pack = BuiltPackage::build(self.move_options.get_package_path()?, build_options) + .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?; + if self.save_metadata { + pack.extract_metadata_and_save()?; + } + let ids = pack + .modules() + .into_iter() + .map(|m| m.self_id().to_string()) + .collect::>(); + Ok(ids) + } +} + +/// Compiles a Move script into bytecode +/// +/// Compiles a script into bytecode and provides a hash of the bytecode. +/// This can then be run with `aptos move run-script` +#[derive(Parser)] +pub struct CompileScript { + #[clap(long, parse(from_os_str))] + pub output_file: Option, + #[clap(flatten)] + pub move_options: MovePackageDir, +} + +#[async_trait] +impl CliCommand for CompileScript { + fn command_name(&self) -> &'static str { + "CompileScript" + } + + async fn execute(self) -> CliTypedResult { + let (bytecode, script_hash) = self.compile_script().await?; + let script_location = self.output_file.unwrap_or_else(|| { + self.move_options + .get_package_path() + .unwrap() + .join("script.mv") + }); + write_to_file(script_location.as_path(), "Script", bytecode.as_slice())?; + Ok(CompileScriptOutput { + script_location, + script_hash, + }) + } +} + +impl CompileScript { + async fn compile_script(&self) -> CliTypedResult<(Vec, HashValue)> { + let build_options = BuildOptions { + install_dir: self.move_options.output_dir.clone(), + ..IncludedArtifacts::None.build_options( + self.move_options.skip_fetch_latest_git_deps, + self.move_options.named_addresses(), + self.move_options.bytecode_version, + ) + }; + let package_dir = self.move_options.get_package_path()?; + let pack = BuiltPackage::build(package_dir, build_options) + .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?; + + let scripts_count = pack.script_count(); + if scripts_count != 1 { + return Err(CliError::UnexpectedError(format!( + "Only one script can be prepared a time. Make sure one and only one script file \ + is included in the Move package. Found {} scripts.", + scripts_count + ))); + } + + let bytecode = pack.extract_script_code().pop().unwrap(); + let script_hash = HashValue::sha3_256_of(bytecode.as_slice()); + Ok((bytecode, script_hash)) + } +} + +#[derive(Debug, Serialize)] +pub struct CompileScriptOutput { + pub script_location: PathBuf, + pub script_hash: HashValue, +} + +/// Runs Move unit tests for a package +/// +/// This will run Move unit tests against a package with debug mode +/// turned on. Note, that move code warnings currently block tests from running. +#[derive(Parser)] +pub struct TestPackage { + /// A filter string to determine which unit tests to run + #[clap(long, short)] + pub filter: Option, + + /// A boolean value to skip warnings. + #[clap(long)] + pub ignore_compile_warnings: bool, + + #[clap(flatten)] + pub(crate) move_options: MovePackageDir, + + /// The maximum number of instructions that can be executed by a test + /// + /// If set, the number of instructions executed by one test will be bounded + // TODO: Remove short, it's against the style guidelines, and update the name here + #[clap( + name = "instructions", + default_value = "100000", + short = 'i', + long = "instructions" + )] + pub instruction_execution_bound: u64, + + /// Collect coverage information for later use with the various `aptos move coverage` subcommands + #[clap(long = "coverage")] + pub compute_coverage: bool, + + /// Dump storage state on failure. + #[clap(long = "dump")] + pub dump_state: bool, +} + +#[async_trait] +impl CliCommand<&'static str> for TestPackage { + fn command_name(&self) -> &'static str { + "TestPackage" + } + + async fn execute(self) -> CliTypedResult<&'static str> { + let mut config = BuildConfig { + additional_named_addresses: self.move_options.named_addresses(), + test_mode: true, + install_dir: self.move_options.output_dir.clone(), + skip_fetch_latest_git_deps: self.move_options.skip_fetch_latest_git_deps, + ..Default::default() + }; + + // Build the Move model for extended checks + let model = &build_model( + self.move_options.get_package_path()?.as_path(), + self.move_options.named_addresses(), + None, + self.move_options.bytecode_version, + )?; + let _ = extended_checks::run_extended_checks(model); + if model.diag_count(Severity::Warning) > 0 { + let mut error_writer = StandardStream::stderr(ColorChoice::Auto); + model.report_diag(&mut error_writer, Severity::Warning); + if model.has_errors() { + return Err(CliError::MoveCompilationError( + "extended checks failed".to_string(), + )); + } + } + let path = self.move_options.get_package_path()?; + let result = move_cli::base::test::run_move_unit_tests( + path.as_path(), + config.clone(), + UnitTestingConfig { + filter: self.filter.clone(), + report_stacktrace_on_abort: true, + report_storage_on_error: self.dump_state, + ignore_compile_warnings: self.ignore_compile_warnings, + ..UnitTestingConfig::default_with_bound(None) + }, + // TODO(Gas): we may want to switch to non-zero costs in the future + aptos_debug_natives::aptos_debug_natives( + NativeGasParameters::zeros(), + AbstractValueSizeGasParameters::zeros(), + ), + None, + self.compute_coverage, + &mut std::io::stdout(), + ) + .map_err(|err| CliError::UnexpectedError(err.to_string()))?; + + // Print coverage summary if --coverage is set + if self.compute_coverage { + config.test_mode = false; + let summary = SummaryCoverage { + summarize_functions: false, + output_csv: false, + filter: self.filter, + move_options: self.move_options, + }; + summary.coverage()?; + + println!("Please use `movement move coverage -h` for more detailed source or bytecode test coverage of this package"); + } + + match result { + UnitTestResult::Success => Ok("Success"), + UnitTestResult::Failure => Err(CliError::MoveTestError), + } + } +} + +#[async_trait] +impl CliCommand<()> for TransactionalTestOpts { + fn command_name(&self) -> &'static str { + "TransactionalTest" + } + + async fn execute(self) -> CliTypedResult<()> { + let root_path = self.root_path.display().to_string(); + + let requirements = vec![transactional_tests_runner::Requirements::new( + run_aptos_test, + "tests".to_string(), + root_path, + self.pattern.clone(), + )]; + + transactional_tests_runner::runner(&self, &requirements) + } +} + +/// Proves a Move package +/// +/// This is a tool for formal verification of a Move package using +/// the Move prover +#[derive(Parser)] +pub struct ProvePackage { + #[clap(flatten)] + move_options: MovePackageDir, + + #[clap(flatten)] + prover_options: ProverOptions, +} + +#[async_trait] +impl CliCommand<&'static str> for ProvePackage { + fn command_name(&self) -> &'static str { + "ProvePackage" + } + + async fn execute(self) -> CliTypedResult<&'static str> { + let ProvePackage { + move_options, + prover_options, + } = self; + + let result = task::spawn_blocking(move || { + prover_options.prove( + move_options.get_package_path()?.as_path(), + move_options.named_addresses(), + move_options.bytecode_version, + ) + }) + .await + .map_err(|err| CliError::UnexpectedError(err.to_string()))?; + match result { + Ok(_) => Ok("Success"), + Err(e) => Err(CliError::MoveProverError(format!("{:#}", e))), + } + } +} + +/// Documents a Move package +/// +/// This converts the content of the package into markdown for documentation. +#[derive(Parser)] +pub struct DocumentPackage { + #[clap(flatten)] + move_options: MovePackageDir, + + #[clap(flatten)] + docgen_options: DocgenOptions, +} + +#[async_trait] +impl CliCommand<&'static str> for DocumentPackage { + fn command_name(&self) -> &'static str { + "DocumentPackage" + } + + async fn execute(self) -> CliTypedResult<&'static str> { + let DocumentPackage { + move_options, + docgen_options, + } = self; + let build_options = BuildOptions { + with_srcs: false, + with_abis: false, + with_source_maps: false, + with_error_map: false, + with_docs: true, + install_dir: None, + named_addresses: move_options.named_addresses(), + docgen_options: Some(docgen_options), + skip_fetch_latest_git_deps: move_options.skip_fetch_latest_git_deps, + bytecode_version: move_options.bytecode_version, + }; + BuiltPackage::build(move_options.get_package_path()?, build_options)?; + Ok("succeeded") + } +} + +#[derive(Parser)] +pub struct IncludedArtifactsArgs { + /// Artifacts to be generated when building the package + /// + /// Which artifacts to include in the package. This can be one of `none`, `sparse`, and + /// `all`. `none` is the most compact form and does not allow to reconstruct a source + /// package from chain; `sparse` is the minimal set of artifacts needed to reconstruct + /// a source package; `all` includes all available artifacts. The choice of included + /// artifacts heavily influences the size and therefore gas cost of publishing: `none` + /// is the size of bytecode alone; `sparse` is roughly 2 times as much; and `all` 3-4 + /// as much. + #[clap(long, default_value_t = IncludedArtifacts::Sparse)] + pub(crate) included_artifacts: IncludedArtifacts, +} + +/// Publishes the modules in a Move package to the Aptos blockchain +#[derive(Parser)] +pub struct PublishPackage { + /// Whether to override the check for maximal size of published data + #[clap(long)] + pub(crate) override_size_check: bool, + + #[clap(flatten)] + pub(crate) included_artifacts_args: IncludedArtifactsArgs, + #[clap(flatten)] + pub(crate) move_options: MovePackageDir, + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[derive(ArgEnum, Clone, Copy, Debug)] +pub enum IncludedArtifacts { + None, + Sparse, + All, +} + +impl Display for IncludedArtifacts { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + use IncludedArtifacts::*; + match self { + None => f.write_str("none"), + Sparse => f.write_str("sparse"), + All => f.write_str("all"), + } + } +} + +impl FromStr for IncludedArtifacts { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + use IncludedArtifacts::*; + match s { + "none" => Ok(None), + "sparse" => Ok(Sparse), + "all" => Ok(All), + _ => Err("unknown variant"), + } + } +} + +impl IncludedArtifacts { + pub(crate) fn build_options( + self, + skip_fetch_latest_git_deps: bool, + named_addresses: BTreeMap, + bytecode_version: Option, + ) -> BuildOptions { + use IncludedArtifacts::*; + match self { + None => BuildOptions { + with_srcs: false, + with_abis: false, + with_source_maps: false, + // Always enable error map bytecode injection + with_error_map: true, + named_addresses, + skip_fetch_latest_git_deps, + bytecode_version, + ..BuildOptions::default() + }, + Sparse => BuildOptions { + with_srcs: true, + with_abis: false, + with_source_maps: false, + with_error_map: true, + named_addresses, + skip_fetch_latest_git_deps, + bytecode_version, + ..BuildOptions::default() + }, + All => BuildOptions { + with_srcs: true, + with_abis: true, + with_source_maps: true, + with_error_map: true, + named_addresses, + skip_fetch_latest_git_deps, + bytecode_version, + ..BuildOptions::default() + }, + } + } +} + +pub const MAX_PUBLISH_PACKAGE_SIZE: usize = 60_000; + +#[async_trait] +impl CliCommand for PublishPackage { + fn command_name(&self) -> &'static str { + "PublishPackage" + } + + async fn execute(self) -> CliTypedResult { + let PublishPackage { + move_options, + txn_options, + override_size_check, + included_artifacts_args, + } = self; + let package_path = move_options.get_package_path()?; + let options = included_artifacts_args.included_artifacts.build_options( + move_options.skip_fetch_latest_git_deps, + move_options.named_addresses(), + move_options.bytecode_version, + ); + let package = BuiltPackage::build(package_path, options)?; + let compiled_units = package.extract_code(); + + // Send the compiled module and metadata using the code::publish_package_txn. + let metadata = package.extract_metadata()?; + let payload = aptos_cached_packages::aptos_stdlib::code_publish_package_txn( + bcs::to_bytes(&metadata).expect("PackageMetadata has BCS"), + compiled_units, + ); + let size = bcs::serialized_size(&payload)?; + println!("package size {} bytes", size); + if !override_size_check && size > MAX_PUBLISH_PACKAGE_SIZE { + return Err(CliError::UnexpectedError(format!( + "The package is larger than {} bytes ({} bytes)! To lower the size \ + you may want to include less artifacts via `--included-artifacts`. \ + You can also override this check with `--override-size-check", + MAX_PUBLISH_PACKAGE_SIZE, size + ))); + } + profile_or_submit(payload, &txn_options).await + } +} + +/// Publishes the modules in a Move package to the Aptos blockchain under a resource account +#[derive(Parser)] +pub struct CreateResourceAccountAndPublishPackage { + /// The named address for compiling and using in the contract + /// + /// This will take the derived account address for the resource account and put it in this location + #[clap(long)] + pub(crate) address_name: String, + + /// Whether to override the check for maximal size of published data + /// + /// This won't bypass on chain checks, so if you are not allowed to go over the size check, it + /// will still be blocked from publishing. + #[clap(long)] + pub(crate) override_size_check: bool, + + #[clap(flatten)] + pub(crate) seed_args: ResourceAccountSeed, + #[clap(flatten)] + pub(crate) included_artifacts_args: IncludedArtifactsArgs, + #[clap(flatten)] + pub(crate) move_options: MovePackageDir, + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for CreateResourceAccountAndPublishPackage { + fn command_name(&self) -> &'static str { + "ResourceAccountPublishPackage" + } + + async fn execute(self) -> CliTypedResult { + let CreateResourceAccountAndPublishPackage { + address_name, + mut move_options, + txn_options, + override_size_check, + included_artifacts_args, + seed_args, + } = self; + + let account = if let Some(Some(account)) = CliConfig::load_profile( + txn_options.profile_options.profile_name(), + ConfigSearchMode::CurrentDirAndParents, + )? + .map(|p| p.account) + { + account + } else { + return Err(CliError::CommandArgumentError( + "Please provide an account using --profile or run movement init".to_string(), + )); + }; + let seed = seed_args.seed()?; + + let resource_address = create_resource_address(account, &seed); + move_options.add_named_address(address_name, resource_address.to_string()); + + let package_path = move_options.get_package_path()?; + let options = included_artifacts_args.included_artifacts.build_options( + move_options.skip_fetch_latest_git_deps, + move_options.named_addresses(), + move_options.bytecode_version, + ); + let package = BuiltPackage::build(package_path, options)?; + let compiled_units = package.extract_code(); + + // Send the compiled module and metadata using the code::publish_package_txn. + let metadata = package.extract_metadata()?; + + let message = format!( + "Do you want to publish this package under the resource account's address {}?", + resource_address + ); + prompt_yes_with_override(&message, txn_options.prompt_options)?; + + let payload = aptos_cached_packages::aptos_stdlib::resource_account_create_resource_account_and_publish_package( + seed, + bcs::to_bytes(&metadata).expect("PackageMetadata has BCS"), + compiled_units, + ); + let size = bcs::serialized_size(&payload)?; + println!("package size {} bytes", size); + if !override_size_check && size > MAX_PUBLISH_PACKAGE_SIZE { + return Err(CliError::UnexpectedError(format!( + "The package is larger than {} bytes ({} bytes)! To lower the size \ + you may want to include less artifacts via `--included-artifacts`. \ + You can also override this check with `--override-size-check", + MAX_PUBLISH_PACKAGE_SIZE, size + ))); + } + txn_options + .submit_transaction(payload) + .await + .map(TransactionSummary::from) + } +} + +/// Downloads a package and stores it in a directory named after the package +/// +/// This lets you retrieve packages directly from the blockchain for inspection +/// and use as a local dependency in testing. +#[derive(Parser)] +pub struct DownloadPackage { + /// Address of the account containing the package + #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] + pub(crate) account: AccountAddress, + + /// Name of the package + #[clap(long)] + pub package: String, + + /// Directory to store downloaded package. Defaults to the current directory. + #[clap(long, parse(from_os_str))] + pub output_dir: Option, + + #[clap(flatten)] + pub(crate) rest_options: RestOptions, + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, +} + +#[async_trait] +impl CliCommand<&'static str> for DownloadPackage { + fn command_name(&self) -> &'static str { + "DownloadPackage" + } + + async fn execute(self) -> CliTypedResult<&'static str> { + let url = self.rest_options.url(&self.profile_options)?; + let registry = CachedPackageRegistry::create(url, self.account).await?; + let output_dir = dir_default_to_current(self.output_dir)?; + + let package = registry + .get_package(self.package) + .await + .map_err(|s| CliError::CommandArgumentError(s.to_string()))?; + if package.upgrade_policy() == UpgradePolicy::arbitrary() { + return Err(CliError::CommandArgumentError( + "A package with upgrade policy `arbitrary` cannot be downloaded \ + since it is not safe to depend on such packages." + .to_owned(), + )); + } + let package_path = output_dir.join(package.name()); + package + .save_package_to_disk(package_path.as_path()) + .map_err(|e| CliError::UnexpectedError(format!("Failed to save package: {}", e)))?; + println!( + "Saved package with {} module(s) to `{}`", + package.module_names().len(), + package_path.display() + ); + Ok("Download succeeded") + } +} + +/// Downloads a package and verifies the bytecode +/// +/// Downloads the package from onchain and verifies the bytecode matches a local compilation of the Move code +#[derive(Parser)] +pub struct VerifyPackage { + /// Address of the account containing the package + #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] + pub(crate) account: AccountAddress, + + /// Artifacts to be generated when building this package. + #[clap(long, default_value_t = IncludedArtifacts::Sparse)] + pub(crate) included_artifacts: IncludedArtifacts, + + #[clap(flatten)] + pub(crate) move_options: MovePackageDir, + #[clap(flatten)] + pub(crate) rest_options: RestOptions, + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, +} + +#[async_trait] +impl CliCommand<&'static str> for VerifyPackage { + fn command_name(&self) -> &'static str { + "DownloadPackage" + } + + async fn execute(self) -> CliTypedResult<&'static str> { + // First build the package locally to get the package metadata + let build_options = BuildOptions { + install_dir: self.move_options.output_dir.clone(), + bytecode_version: self.move_options.bytecode_version, + ..self.included_artifacts.build_options( + self.move_options.skip_fetch_latest_git_deps, + self.move_options.named_addresses(), + self.move_options.bytecode_version, + ) + }; + let pack = BuiltPackage::build(self.move_options.get_package_path()?, build_options) + .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?; + let compiled_metadata = pack.extract_metadata()?; + + // Now pull the compiled package + let url = self.rest_options.url(&self.profile_options)?; + let registry = CachedPackageRegistry::create(url, self.account).await?; + let package = registry + .get_package(pack.name()) + .await + .map_err(|s| CliError::CommandArgumentError(s.to_string()))?; + + // We can't check the arbitrary, because it could change on us + if package.upgrade_policy() == UpgradePolicy::arbitrary() { + return Err(CliError::CommandArgumentError( + "A package with upgrade policy `arbitrary` cannot be downloaded \ + since it is not safe to depend on such packages." + .to_owned(), + )); + } + + // Verify that the source digest matches + package.verify(&compiled_metadata)?; + + Ok("Successfully verified source of package") + } +} + +/// Lists information about packages and modules on-chain for an account +#[derive(Parser)] +pub struct ListPackage { + /// Address of the account for which to list packages. + #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] + pub(crate) account: AccountAddress, + + /// Type of items to query + /// + /// Current supported types `[packages]` + #[clap(long, default_value_t = MoveListQuery::Packages)] + query: MoveListQuery, + + #[clap(flatten)] + rest_options: RestOptions, + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, +} + +#[derive(ArgEnum, Clone, Copy, Debug)] +pub enum MoveListQuery { + Packages, +} + +impl Display for MoveListQuery { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + MoveListQuery::Packages => "packages", + }) + } +} + +impl FromStr for MoveListQuery { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "packages" => Ok(MoveListQuery::Packages), + _ => Err("Invalid query. Valid values are modules, packages"), + } + } +} + +#[async_trait] +impl CliCommand<&'static str> for ListPackage { + fn command_name(&self) -> &'static str { + "ListPackage" + } + + async fn execute(self) -> CliTypedResult<&'static str> { + let url = self.rest_options.url(&self.profile_options)?; + let registry = CachedPackageRegistry::create(url, self.account).await?; + match self.query { + MoveListQuery::Packages => { + for name in registry.package_names() { + let data = registry.get_package(name).await?; + println!("package {}", data.name()); + println!(" upgrade_policy: {}", data.upgrade_policy()); + println!(" upgrade_number: {}", data.upgrade_number()); + println!(" source_digest: {}", data.source_digest()); + println!(" modules: {}", data.module_names().into_iter().join(", ")); + } + }, + } + Ok("list succeeded") + } +} + +/// Cleans derived artifacts of a package. +#[derive(Parser)] +pub struct CleanPackage { + #[clap(flatten)] + pub(crate) move_options: MovePackageDir, + #[clap(flatten)] + pub(crate) prompt_options: PromptOptions, +} + +#[async_trait] +impl CliCommand<&'static str> for CleanPackage { + fn command_name(&self) -> &'static str { + "CleanPackage" + } + + async fn execute(self) -> CliTypedResult<&'static str> { + let path = self.move_options.get_package_path()?; + let build_dir = path.join("build"); + // Only remove the build dir if it exists, allowing for users to still clean their cache + if build_dir.exists() { + std::fs::remove_dir_all(build_dir.as_path()) + .map_err(|e| CliError::IO(build_dir.display().to_string(), e))?; + } + + let move_dir = PathBuf::from(MOVE_HOME.as_str()); + if move_dir.exists() + && prompt_yes_with_override( + &format!( + "Do you also want to delete the local package download cache at `{}`?", + move_dir.display() + ), + self.prompt_options, + ) + .is_ok() + { + std::fs::remove_dir_all(move_dir.as_path()) + .map_err(|e| CliError::IO(move_dir.display().to_string(), e))?; + } + Ok("succeeded") + } +} + +/// Run a Move function +#[derive(Parser)] +pub struct RunFunction { + #[clap(flatten)] + pub(crate) entry_function_args: EntryFunctionArguments, + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for RunFunction { + fn command_name(&self) -> &'static str { + "RunFunction" + } + + async fn execute(self) -> CliTypedResult { + let payload = TransactionPayload::EntryFunction( + self.entry_function_args.create_entry_function_payload()?, + ); + profile_or_submit(payload, &self.txn_options).await + } +} + +/// Run a view function +#[derive(Parser)] +pub struct ViewFunction { + #[clap(flatten)] + pub(crate) entry_function_args: EntryFunctionArguments, + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand> for ViewFunction { + fn command_name(&self) -> &'static str { + "RunViewFunction" + } + + async fn execute(self) -> CliTypedResult> { + let mut args: Vec = vec![]; + for arg in self.entry_function_args.arg_vec.args { + args.push(arg.to_json()?); + } + + let view_request = ViewRequest { + function: EntryFunctionId { + module: self.entry_function_args.function_id.module_id.into(), + name: self.entry_function_args.function_id.member_id.into(), + }, + type_arguments: self.entry_function_args.type_args, + arguments: args, + }; + + self.txn_options.view(view_request).await + } +} + +/// Run a Move script +#[derive(Parser)] +pub struct RunScript { + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, + #[clap(flatten)] + pub(crate) compile_proposal_args: CompileScriptFunction, + #[clap(flatten)] + pub(crate) arg_vec: ArgWithTypeVec, + /// TypeTag arguments separated by spaces. + /// + /// Example: `u8 u16 u32 u64 u128 u256 bool address vector signer` + #[clap(long, multiple_values = true)] + pub(crate) type_args: Vec, +} + +#[async_trait] +impl CliCommand for RunScript { + fn command_name(&self) -> &'static str { + "RunScript" + } + + async fn execute(self) -> CliTypedResult { + let (bytecode, _script_hash) = self + .compile_proposal_args + .compile("RunScript", self.txn_options.prompt_options)?; + + let mut args: Vec = vec![]; + for arg in self.arg_vec.args { + args.push(arg.try_into()?); + } + + let mut type_args: Vec = Vec::new(); + + // These TypeArgs are used for generics + for type_arg in self.type_args.into_iter() { + let type_tag = TypeTag::try_from(type_arg) + .map_err(|err| CliError::UnableToParse("--type-args", err.to_string()))?; + type_args.push(type_tag) + } + + let payload = TransactionPayload::Script(Script::new(bytecode, type_args, args)); + + profile_or_submit(payload, &self.txn_options).await + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) enum FunctionArgType { + Address, + Bool, + Hex, + String, + U8, + U16, + U32, + U64, + U128, + U256, + Raw, +} + +impl Display for FunctionArgType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + FunctionArgType::Address => write!(f, "address"), + FunctionArgType::Bool => write!(f, "bool"), + FunctionArgType::Hex => write!(f, "hex"), + FunctionArgType::String => write!(f, "string"), + FunctionArgType::U8 => write!(f, "u8"), + FunctionArgType::U16 => write!(f, "u16"), + FunctionArgType::U32 => write!(f, "u32"), + FunctionArgType::U64 => write!(f, "u64"), + FunctionArgType::U128 => write!(f, "u128"), + FunctionArgType::U256 => write!(f, "u256"), + FunctionArgType::Raw => write!(f, "raw"), + } + } +} + +impl FunctionArgType { + /// Parse a standalone argument (not a vector) from string slice into BCS representation. + fn parse_arg_str(&self, arg: &str) -> CliTypedResult> { + match self { + FunctionArgType::Address => bcs::to_bytes( + &load_account_arg(arg) + .map_err(|err| CliError::UnableToParse("address", err.to_string()))?, + ) + .map_err(|err| CliError::BCS("arg", err)), + FunctionArgType::Bool => bcs::to_bytes( + &bool::from_str(arg) + .map_err(|err| CliError::UnableToParse("bool", err.to_string()))?, + ) + .map_err(|err| CliError::BCS("arg", err)), + FunctionArgType::Hex => bcs::to_bytes( + &hex::decode(arg).map_err(|err| CliError::UnableToParse("hex", err.to_string()))?, + ) + .map_err(|err| CliError::BCS("arg", err)), + FunctionArgType::String => bcs::to_bytes(arg).map_err(|err| CliError::BCS("arg", err)), + FunctionArgType::U8 => bcs::to_bytes( + &u8::from_str(arg).map_err(|err| CliError::UnableToParse("u8", err.to_string()))?, + ) + .map_err(|err| CliError::BCS("arg", err)), + FunctionArgType::U16 => bcs::to_bytes( + &u16::from_str(arg) + .map_err(|err| CliError::UnableToParse("u16", err.to_string()))?, + ) + .map_err(|err| CliError::BCS("arg", err)), + FunctionArgType::U32 => bcs::to_bytes( + &u32::from_str(arg) + .map_err(|err| CliError::UnableToParse("u32", err.to_string()))?, + ) + .map_err(|err| CliError::BCS("arg", err)), + FunctionArgType::U64 => bcs::to_bytes( + &u64::from_str(arg) + .map_err(|err| CliError::UnableToParse("u64", err.to_string()))?, + ) + .map_err(|err| CliError::BCS("arg", err)), + FunctionArgType::U128 => bcs::to_bytes( + &u128::from_str(arg) + .map_err(|err| CliError::UnableToParse("u128", err.to_string()))?, + ) + .map_err(|err| CliError::BCS("arg", err)), + FunctionArgType::U256 => bcs::to_bytes( + &U256::from_str(arg) + .map_err(|err| CliError::UnableToParse("u256", err.to_string()))?, + ) + .map_err(|err| CliError::BCS("arg", err)), + FunctionArgType::Raw => { + hex::decode(arg).map_err(|err| CliError::UnableToParse("raw", err.to_string())) + }, + } + } + + /// Recursively parse argument JSON into BCS representation. + pub fn parse_arg_json(&self, arg: &serde_json::Value) -> CliTypedResult { + match arg { + serde_json::Value::Bool(value) => Ok(ArgWithType { + _ty: self.clone(), + _vector_depth: 0, + arg: self.parse_arg_str(value.to_string().as_str())?, + }), + serde_json::Value::Number(value) => Ok(ArgWithType { + _ty: self.clone(), + _vector_depth: 0, + arg: self.parse_arg_str(value.to_string().as_str())?, + }), + serde_json::Value::String(value) => Ok(ArgWithType { + _ty: self.clone(), + _vector_depth: 0, + arg: self.parse_arg_str(value.as_str())?, + }), + serde_json::Value::Array(_) => { + let mut bcs: Vec = vec![]; // BCS representation of argument. + let mut common_sub_arg_depth = None; + // Prepend argument sequence length to BCS bytes vector. + write_u64_as_uleb128(&mut bcs, arg.as_array().unwrap().len()); + // Loop over all of the vector's sub-arguments, which may also be vectors: + for sub_arg in arg.as_array().unwrap() { + let ArgWithType { + _ty: _, + _vector_depth: sub_arg_depth, + arg: mut sub_arg_bcs, + } = self.parse_arg_json(sub_arg)?; + // Verify all sub-arguments have same depth. + if let Some(check_depth) = common_sub_arg_depth { + if check_depth != sub_arg_depth { + return Err(CliError::CommandArgumentError( + "Variable vector depth".to_string(), + )); + } + }; + common_sub_arg_depth = Some(sub_arg_depth); + bcs.append(&mut sub_arg_bcs); // Append sub-argument BCS. + } + // Default sub-argument depth is 0 for when no sub-arguments were looped over. + Ok(ArgWithType { + _ty: self.clone(), + _vector_depth: common_sub_arg_depth.unwrap_or(0) + 1, + arg: bcs, + }) + }, + serde_json::Value::Null => { + Err(CliError::CommandArgumentError("Null argument".to_string())) + }, + serde_json::Value::Object(_) => Err(CliError::CommandArgumentError( + "JSON object argument".to_string(), + )), + } + } +} + +// TODO use from move_binary_format::file_format_common if it is made public. +fn write_u64_as_uleb128(binary: &mut Vec, mut val: usize) { + loop { + let cur = val & 0x7F; + if cur != val { + binary.push((cur | 0x80) as u8); + val >>= 7; + } else { + binary.push(cur as u8); + break; + } + } +} + +impl FromStr for FunctionArgType { + type Err = CliError; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "address" => Ok(FunctionArgType::Address), + "bool" => Ok(FunctionArgType::Bool), + "hex" => Ok(FunctionArgType::Hex), + "string" => Ok(FunctionArgType::String), + "u8" => Ok(FunctionArgType::U8), + "u16" => Ok(FunctionArgType::U16), + "u32" => Ok(FunctionArgType::U32), + "u64" => Ok(FunctionArgType::U64), + "u128" => Ok(FunctionArgType::U128), + "u256" => Ok(FunctionArgType::U256), + "raw" => Ok(FunctionArgType::Raw), + str => {Err(CliError::CommandArgumentError(format!( + "Invalid arg type '{}'. Must be one of: ['{}','{}','{}','{}','{}','{}','{}','{}','{}','{}','{}']", + str, + FunctionArgType::Address, + FunctionArgType::Bool, + FunctionArgType::Hex, + FunctionArgType::String, + FunctionArgType::U8, + FunctionArgType::U16, + FunctionArgType::U32, + FunctionArgType::U64, + FunctionArgType::U128, + FunctionArgType::U256, + FunctionArgType::Raw))) + } + } + } +} + +/// A parseable arg with a type separated by a colon +#[derive(Debug)] +pub struct ArgWithType { + pub(crate) _ty: FunctionArgType, + pub(crate) _vector_depth: u8, + pub(crate) arg: Vec, +} + +impl ArgWithType { + pub fn address(account_address: AccountAddress) -> Self { + ArgWithType { + _ty: FunctionArgType::Address, + _vector_depth: 0, + arg: bcs::to_bytes(&account_address).unwrap(), + } + } + + pub fn u64(arg: u64) -> Self { + ArgWithType { + _ty: FunctionArgType::U64, + _vector_depth: 0, + arg: bcs::to_bytes(&arg).unwrap(), + } + } + + pub fn bytes(arg: Vec) -> Self { + ArgWithType { + _ty: FunctionArgType::Raw, + _vector_depth: 0, + arg: bcs::to_bytes(&arg).unwrap(), + } + } + + pub fn raw(arg: Vec) -> Self { + ArgWithType { + _ty: FunctionArgType::Raw, + _vector_depth: 0, + arg, + } + } + + pub fn bcs_value_to_json<'a, T: Deserialize<'a> + Serialize>( + &'a self, + ) -> CliTypedResult { + match self._vector_depth { + 0 => serde_json::to_value(bcs::from_bytes::(&self.arg)?) + .map_err(|err| CliError::UnexpectedError(err.to_string())), + 1 => serde_json::to_value(bcs::from_bytes::>(&self.arg)?) + .map_err(|err| CliError::UnexpectedError(err.to_string())), + + 2 => serde_json::to_value(bcs::from_bytes::>>(&self.arg)?) + .map_err(|err| CliError::UnexpectedError(err.to_string())), + + 3 => serde_json::to_value(bcs::from_bytes::>>>(&self.arg)?) + .map_err(|err| CliError::UnexpectedError(err.to_string())), + + 4 => serde_json::to_value(bcs::from_bytes::>>>>(&self.arg)?) + .map_err(|err| CliError::UnexpectedError(err.to_string())), + 5 => serde_json::to_value(bcs::from_bytes::>>>>>(&self.arg)?) + .map_err(|err| CliError::UnexpectedError(err.to_string())), + 6 => serde_json::to_value(bcs::from_bytes::>>>>>>( + &self.arg, + )?) + .map_err(|err| CliError::UnexpectedError(err.to_string())), + 7 => serde_json::to_value(bcs::from_bytes::>>>>>>>( + &self.arg, + )?) + .map_err(|err| CliError::UnexpectedError(err.to_string())), + depth => Err(CliError::UnexpectedError(format!( + "Vector of depth {depth} is overly nested" + ))), + } + } + + pub fn to_json(&self) -> CliTypedResult { + match self._ty { + FunctionArgType::Address => self.bcs_value_to_json::(), + FunctionArgType::Bool => self.bcs_value_to_json::(), + FunctionArgType::Hex => self.bcs_value_to_json::>(), + FunctionArgType::String => self.bcs_value_to_json::(), + FunctionArgType::U8 => self.bcs_value_to_json::(), + FunctionArgType::U16 => self.bcs_value_to_json::(), + FunctionArgType::U32 => self.bcs_value_to_json::(), + FunctionArgType::U64 => self.bcs_value_to_json::(), + FunctionArgType::U128 => self.bcs_value_to_json::(), + FunctionArgType::U256 => self.bcs_value_to_json::(), + FunctionArgType::Raw => serde_json::to_value(&self.arg) + .map_err(|err| CliError::UnexpectedError(err.to_string())), + } + .map_err(|err| { + CliError::UnexpectedError(format!("Failed to parse argument to JSON {}", err)) + }) + } +} + +/// Does not support string arguments that contain the following characters: +/// +/// * `,` +/// * `[` +/// * `]` +impl FromStr for ArgWithType { + type Err = CliError; + + fn from_str(s: &str) -> Result { + // Splits on the first colon, returning at most `2` elements + // This is required to support args that contain a colon + let parts: Vec<_> = s.splitn(2, ':').collect(); + if parts.len() != 2 { + return Err(CliError::CommandArgumentError( + "Arguments must be pairs of : e.g. bool:true".to_string(), + )); + } + let ty = FunctionArgType::from_str(parts.first().unwrap())?; + let mut arg = String::from(*parts.last().unwrap()); + // May need to surround with quotes if not an array, so arg can be parsed into JSON. + if !arg.starts_with('[') { + if let FunctionArgType::Address + | FunctionArgType::Hex + | FunctionArgType::String + | FunctionArgType::Raw = ty + { + arg = format!("\"{}\"", arg); + } + } + let json = serde_json::from_str::(arg.as_str()) + .map_err(|err| CliError::UnexpectedError(err.to_string()))?; + ty.parse_arg_json(&json) + } +} + +impl TryInto for ArgWithType { + type Error = CliError; + + fn try_into(self) -> Result { + if self._vector_depth > 0 && self._ty != FunctionArgType::U8 { + return Err(CliError::UnexpectedError( + "Unable to parse non-u8 vector to transaction argument".to_string(), + )); + } + match self._ty { + FunctionArgType::Address => Ok(TransactionArgument::Address(txn_arg_parser( + &self.arg, "address", + )?)), + FunctionArgType::Bool => Ok(TransactionArgument::Bool(txn_arg_parser( + &self.arg, "bool", + )?)), + FunctionArgType::Hex => Ok(TransactionArgument::U8Vector(txn_arg_parser( + &self.arg, "hex", + )?)), + FunctionArgType::String => Ok(TransactionArgument::U8Vector(txn_arg_parser( + &self.arg, "string", + )?)), + FunctionArgType::U8 => match self._vector_depth { + 0 => Ok(TransactionArgument::U8(txn_arg_parser(&self.arg, "u8")?)), + 1 => Ok(TransactionArgument::U8Vector(txn_arg_parser( + &self.arg, + "vector", + )?)), + depth => Err(CliError::UnexpectedError(format!( + "Unable to parse u8 vector of depth {} to transaction argument", + depth + ))), + }, + FunctionArgType::U16 => Ok(TransactionArgument::U16(txn_arg_parser(&self.arg, "u16")?)), + FunctionArgType::U32 => Ok(TransactionArgument::U32(txn_arg_parser(&self.arg, "u32")?)), + FunctionArgType::U64 => Ok(TransactionArgument::U64(txn_arg_parser(&self.arg, "u64")?)), + FunctionArgType::U128 => Ok(TransactionArgument::U128(txn_arg_parser( + &self.arg, "u128", + )?)), + FunctionArgType::U256 => Ok(TransactionArgument::U256(txn_arg_parser( + &self.arg, "u256", + )?)), + FunctionArgType::Raw => Ok(TransactionArgument::U8Vector(txn_arg_parser( + &self.arg, "raw", + )?)), + } + } +} + +fn txn_arg_parser( + data: &[u8], + label: &'static str, +) -> Result { + bcs::from_bytes(data).map_err(|err| CliError::UnableToParse(label, err.to_string())) +} + +/// Identifier of a module member (function or struct). +#[derive(Debug, Clone)] +pub struct MemberId { + pub module_id: ModuleId, + pub member_id: Identifier, +} + +fn parse_member_id(function_id: &str) -> CliTypedResult { + let ids: Vec<&str> = function_id.split_terminator("::").collect(); + if ids.len() != 3 { + return Err(CliError::CommandArgumentError( + "FunctionId is not well formed. Must be of the form
::::" + .to_string(), + )); + } + let address = load_account_arg(ids.first().unwrap())?; + let module = Identifier::from_str(ids.get(1).unwrap()) + .map_err(|err| CliError::UnableToParse("Module Name", err.to_string()))?; + let member_id = Identifier::from_str(ids.get(2).unwrap()) + .map_err(|err| CliError::UnableToParse("Member Name", err.to_string()))?; + let module_id = ModuleId::new(address, module); + Ok(MemberId { + module_id, + member_id, + }) +} + +impl FromStr for MemberId { + type Err = CliError; + + fn from_str(s: &str) -> Result { + parse_member_id(s) + } +} diff --git a/movement-sdk/clis/aptos/src/move_tool/package_hooks.rs b/movement-sdk/clis/aptos/src/move_tool/package_hooks.rs new file mode 100644 index 000000000..208d368c3 --- /dev/null +++ b/movement-sdk/clis/aptos/src/move_tool/package_hooks.rs @@ -0,0 +1,54 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{common::types::load_account_arg, move_tool::CachedPackageRegistry}; +use aptos_framework::UPGRADE_POLICY_CUSTOM_FIELD; +use futures::executor::block_on; +use move_package::{ + compilation::package_layout::CompiledPackageLayout, package_hooks::PackageHooks, + source_package::parsed_manifest::CustomDepInfo, +}; +use move_symbol_pool::Symbol; +use reqwest::Url; + +pub fn register_package_hooks() { + move_package::package_hooks::register_package_hooks(Box::new(AptosPackageHooks {})) +} + +struct AptosPackageHooks {} + +impl PackageHooks for AptosPackageHooks { + fn custom_package_info_fields(&self) -> Vec { + vec![UPGRADE_POLICY_CUSTOM_FIELD.to_string()] + } + + fn custom_dependency_key(&self) -> Option { + Some("movement".to_string()) + } + + fn resolve_custom_dependency( + &self, + _dep_name: Symbol, + info: &CustomDepInfo, + ) -> anyhow::Result<()> { + block_on(maybe_download_package(info)) + } +} + +async fn maybe_download_package(info: &CustomDepInfo) -> anyhow::Result<()> { + if !info + .download_to + .join(CompiledPackageLayout::BuildInfo.path()) + .exists() + { + let registry = CachedPackageRegistry::create( + Url::parse(info.node_url.as_str())?, + load_account_arg(info.package_address.as_str())?, + ) + .await?; + let package = registry.get_package(info.package_name).await?; + package.save_package_to_disk(info.download_to.as_path()) + } else { + Ok(()) + } +} diff --git a/movement-sdk/clis/aptos/src/move_tool/show.rs b/movement-sdk/clis/aptos/src/move_tool/show.rs new file mode 100644 index 000000000..e1cc0e726 --- /dev/null +++ b/movement-sdk/clis/aptos/src/move_tool/show.rs @@ -0,0 +1,109 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::IncludedArtifactsArgs; +use crate::common::types::{CliCommand, CliError, CliResult, CliTypedResult, MovePackageDir}; +use anyhow::Context; +use aptos_framework::{BuildOptions, BuiltPackage}; +use aptos_types::transaction::EntryABI; +use async_trait::async_trait; +use clap::{Parser, Subcommand}; + +#[derive(Subcommand)] +pub enum ShowTool { + Abi(ShowAbi), +} + +impl ShowTool { + pub async fn execute_serialized(self) -> CliResult { + match self { + Self::Abi(tool) => tool.execute_serialized().await, + } + } +} + +/// Compile the package and show information about the ABIs of the compiled modules. +/// +/// For example, this would show the function `transfer` in the module `coin`: +/// +/// aptos move show abi --modules coin --names transfer +/// +#[derive(Parser)] +pub struct ShowAbi { + /// If provided, only show items from the given Move modules. These should be module + /// names, not file paths. For example, `coin`. + #[clap(long, multiple_values = true)] + modules: Vec, + + /// If provided, only show items with the given names. For example, `transfer`. + #[clap(long, multiple_values = true)] + names: Vec, + + #[clap(flatten)] + included_artifacts_args: IncludedArtifactsArgs, + + #[clap(flatten)] + move_options: MovePackageDir, +} + +#[async_trait] +impl CliCommand> for ShowAbi { + fn command_name(&self) -> &'static str { + "ShowAbi" + } + + async fn execute(self) -> CliTypedResult> { + let build_options = BuildOptions { + install_dir: self.move_options.output_dir.clone(), + with_abis: true, + ..self + .included_artifacts_args + .included_artifacts + .build_options( + self.move_options.skip_fetch_latest_git_deps, + self.move_options.named_addresses(), + self.move_options.bytecode_version, + ) + }; + + // Build the package. + let package = BuiltPackage::build(self.move_options.get_package_path()?, build_options) + .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?; + + // Get ABIs from the package. + let abis = package + .extract_abis() + .context("No ABIs found after compilation")?; + + // Filter the ABIs based on the filters passed in. + let abis = abis + .into_iter() + .filter(|abi| { + let name = abi.name().to_string(); + if !self.names.is_empty() && !self.names.contains(&name) { + return false; + } + match &abi { + EntryABI::EntryFunction(func) => { + if !self.modules.is_empty() + && !self + .modules + .contains(&func.module_name().name().to_string()) + { + return false; + } + }, + EntryABI::TransactionScript(_) => { + // If there were any modules specified we ignore scripts. + if !self.modules.is_empty() { + return false; + } + }, + } + true + }) + .collect(); + + Ok(abis) + } +} diff --git a/movement-sdk/clis/aptos/src/move_tool/stored_package.rs b/movement-sdk/clis/aptos/src/move_tool/stored_package.rs new file mode 100644 index 000000000..c957c4c55 --- /dev/null +++ b/movement-sdk/clis/aptos/src/move_tool/stored_package.rs @@ -0,0 +1,214 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::bail; +use aptos_framework::{ + natives::code::{ModuleMetadata, PackageMetadata, PackageRegistry, UpgradePolicy}, + unzip_metadata_str, +}; +use aptos_rest_client::Client; +use aptos_types::account_address::AccountAddress; +use move_package::compilation::package_layout::CompiledPackageLayout; +use reqwest::Url; +use std::{fs, path::Path}; + +// TODO: this is a first naive implementation of the package registry. Before mainnet +// we need to use tables for the package registry. + +/// Represents the package registry at a given account. +pub struct CachedPackageRegistry { + inner: PackageRegistry, +} + +/// Represents the package metadata found in an registry. +pub struct CachedPackageMetadata<'a> { + metadata: &'a PackageMetadata, +} + +/// Represents the package metadata found in an registry. +pub struct CachedModuleMetadata<'a> { + metadata: &'a ModuleMetadata, +} + +impl CachedPackageRegistry { + /// Creates a new registry. + pub async fn create(url: Url, addr: AccountAddress) -> anyhow::Result { + let client = Client::new(url); + // Need to use a different type to deserialize JSON + let inner = client + .get_account_resource_bcs::(addr, "0x1::code::PackageRegistry") + .await? + .into_inner(); + Ok(Self { inner }) + } + + /// Returns the list of packages in this registry by name. + pub fn package_names(&self) -> Vec<&str> { + self.inner + .packages + .iter() + .map(|p| p.name.as_str()) + .collect() + } + + /// Finds the metadata for the given module in the registry by its unique name. + pub async fn get_module<'a>( + &self, + name: impl AsRef, + ) -> anyhow::Result> { + let name = name.as_ref(); + for package in &self.inner.packages { + for module in &package.modules { + if module.name == name { + return Ok(CachedModuleMetadata { metadata: module }); + } + } + } + bail!("module `{}` not found", name) + } + + /// Finds the metadata for the given package in the registry by its unique name. + pub async fn get_package<'a>( + &self, + name: impl AsRef, + ) -> anyhow::Result> { + let name = name.as_ref(); + for package in &self.inner.packages { + if package.name == name { + return Ok(CachedPackageMetadata { metadata: package }); + } + } + bail!("package `{}` not found", name) + } +} + +impl<'a> CachedPackageMetadata<'a> { + pub fn name(&self) -> &str { + &self.metadata.name + } + + pub fn upgrade_policy(&self) -> UpgradePolicy { + self.metadata.upgrade_policy + } + + pub fn upgrade_number(&self) -> u64 { + self.metadata.upgrade_number + } + + pub fn source_digest(&self) -> &str { + &self.metadata.source_digest + } + + pub fn manifest(&self) -> anyhow::Result { + unzip_metadata_str(&self.metadata.manifest) + } + + pub fn module_names(&self) -> Vec<&str> { + self.metadata + .modules + .iter() + .map(|s| s.name.as_str()) + .collect() + } + + pub fn module(&self, name: impl AsRef) -> anyhow::Result> { + let name = name.as_ref(); + for module in &self.metadata.modules { + if module.name == name { + return Ok(CachedModuleMetadata { metadata: module }); + } + } + bail!("module `{}` not found", name) + } + + pub fn save_package_to_disk(&self, path: &Path) -> anyhow::Result<()> { + fs::create_dir_all(path)?; + fs::write( + path.join("Move.toml"), + unzip_metadata_str(&self.metadata.manifest)?, + )?; + let sources_dir = path.join(CompiledPackageLayout::Sources.path()); + fs::create_dir_all(&sources_dir)?; + for module in &self.metadata.modules { + let source = match module.source.is_empty() { + true => { + println!("module without code: {}", module.name); + "".into() + }, + false => unzip_metadata_str(&module.source)?, + }; + fs::write(sources_dir.join(format!("{}.move", module.name)), source)?; + } + Ok(()) + } + + pub fn verify(&self, package_metadata: &PackageMetadata) -> anyhow::Result<()> { + let self_metadata = self.metadata; + + if self_metadata.name != package_metadata.name { + bail!( + "Package name doesn't match {} : {}", + package_metadata.name, + self_metadata.name + ) + } else if self_metadata.deps != package_metadata.deps { + bail!( + "Dependencies don't match {:?} : {:?}", + package_metadata.deps, + self_metadata.deps + ) + } else if self_metadata.modules != package_metadata.modules { + bail!( + "Modules don't match {:?} : {:?}", + package_metadata.modules, + self_metadata.modules + ) + } else if self_metadata.manifest != package_metadata.manifest { + bail!( + "Manifest doesn't match {:?} : {:?}", + package_metadata.manifest, + self_metadata.manifest + ) + } else if self_metadata.upgrade_policy != package_metadata.upgrade_policy { + bail!( + "Upgrade policy doesn't match {:?} : {:?}", + package_metadata.upgrade_policy, + self_metadata.upgrade_policy + ) + } else if self_metadata.upgrade_number != package_metadata.upgrade_number { + bail!( + "Upgrade number doesn't match {:?} : {:?}", + package_metadata.upgrade_number, + self_metadata.upgrade_number + ) + } else if self_metadata.extension != package_metadata.extension { + bail!( + "Extensions doesn't match {:?} : {:?}", + package_metadata.extension, + self_metadata.extension + ) + } else if self_metadata.source_digest != package_metadata.source_digest { + bail!( + "Source digests doesn't match {:?} : {:?}", + package_metadata.source_digest, + self_metadata.source_digest + ) + } + + Ok(()) + } +} + +impl<'a> CachedModuleMetadata<'a> { + pub fn name(&self) -> &str { + &self.metadata.name + } + + pub fn zipped_source(&self) -> &[u8] { + &self.metadata.source + } + + pub fn zipped_source_map_raw(&self) -> &[u8] { + &self.metadata.source_map + } +} diff --git a/movement-sdk/clis/aptos/src/move_tool/transactional_tests_runner.rs b/movement-sdk/clis/aptos/src/move_tool/transactional_tests_runner.rs new file mode 100644 index 000000000..91b504a47 --- /dev/null +++ b/movement-sdk/clis/aptos/src/move_tool/transactional_tests_runner.rs @@ -0,0 +1,345 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::types::{CliError, CliTypedResult}; +/// Most of the code below comes from the crate `datatest-stable`. Because the limitation of `datatest-stable`, +/// we are not able to run transactional tests as a subcommand of the Movement CLI. Therefore, we need to duplicate code +/// here and make minor modifications. +/// +use clap::Parser; +use std::{ + io::{self, Write}, + num::NonZeroUsize, + panic::{catch_unwind, AssertUnwindSafe}, + path::{Path, PathBuf}, + process, + sync::mpsc::{channel, Sender}, + thread, +}; +use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; + +type Result = std::result::Result>; + +/// Run Move transactional tests +#[derive(Parser, Clone)] +pub struct TransactionalTestOpts { + /// The filter string is tested against the name of all tests, and only those tests whose names + /// contain the filter are run. + #[clap(long)] + pub filter: Option, + + /// Exactly match filters rather than match by substring + #[clap(long = "exact")] + pub filter_exact: bool, + + /// Number of threads used for running tests in parallel + #[clap(long, default_value = "32")] + pub test_threads: NonZeroUsize, + + /// Output minimal information + #[clap(long)] + pub quiet: bool, + + /// List all tests + #[clap(long)] + pub list: bool, + + /// Path to contain the tests + #[clap(long, parse(from_os_str))] + pub root_path: PathBuf, + + /// Pattern to match the test files + #[clap(long, default_value = r".*\.(mvir|move)$")] + pub pattern: String, +} + +/// Helper function to iterate through all the files in the given directory, skipping hidden files, +/// and return an iterator of their paths. +pub fn iterate_directory(path: &Path) -> impl Iterator { + walkdir::WalkDir::new(path) + .into_iter() + .map(::std::result::Result::unwrap) + .filter(|entry| { + entry.file_type().is_file() + && entry + .file_name() + .to_str() + .map_or(false, |s| !s.starts_with('.')) // Skip hidden files + }) + .map(|entry| entry.path().to_path_buf()) +} + +pub fn derive_test_name(root: &Path, path: &Path, test_name: &str) -> String { + let relative = path.strip_prefix(root).unwrap_or_else(|_| { + panic!( + "failed to strip prefix '{}' from path '{}'", + root.display(), + path.display() + ) + }); + let mut test_name = test_name.to_string(); + test_name = format!("{}::{}", test_name, relative.display()); + test_name +} + +struct Test { + testfn: Box Result<()> + Send>, + name: String, +} + +enum TestResult { + Ok, + Failed, + FailedWithMsg(String), +} + +pub(crate) fn runner(options: &TransactionalTestOpts, reqs: &[Requirements]) -> CliTypedResult<()> { + let mut tests: Vec = reqs.iter().flat_map(|req| req.expand()).collect(); + tests.sort_by(|a, b| a.name.cmp(&b.name)); + + if options.list { + for test in &tests { + println!("{}: test", test.name); + } + + return Ok(()); + } + + match run_tests(options, tests) { + Ok(true) => Ok(()), + Ok(false) => process::exit(101), + Err(e) => Err(CliError::UnexpectedError(format!( + "error: io error when running tests: {:?}", + e + ))), + } +} + +fn run_tests(options: &TransactionalTestOpts, tests: Vec) -> io::Result { + let total = tests.len(); + + // Filter out tests + let mut remaining = match &options.filter { + None => tests, + Some(filter) => tests + .into_iter() + .filter(|test| { + if options.filter_exact { + test.name == filter[..] + } else { + test.name.contains(&filter[..]) + } + }) + .rev() + .collect(), + }; + + let filtered_out = total - remaining.len(); + let mut summary = TestSummary::new(total, filtered_out); + + if !options.quiet { + summary.write_starting_msg()?; + } + + let (tx, rx) = channel(); + + let mut pending = 0; + while pending > 0 || !remaining.is_empty() { + while pending < options.test_threads.get() && !remaining.is_empty() { + let test = remaining.pop().unwrap(); + run_test(test, tx.clone()); + pending += 1; + } + + let (name, result) = rx.recv().unwrap(); + summary.handle_result(name, result)?; + + pending -= 1; + } + + // Write Test Summary + if !options.quiet { + summary.write_summary()?; + } + + Ok(summary.success()) +} + +fn run_test(test: Test, channel: Sender<(String, TestResult)>) { + let Test { name, testfn } = test; + + let cfg = thread::Builder::new().name(name.clone()); + cfg.spawn(move || { + let result = match catch_unwind(AssertUnwindSafe(testfn)) { + Ok(Ok(())) => TestResult::Ok, + Ok(Err(e)) => TestResult::FailedWithMsg(format!("{:?}", e)), + Err(_) => TestResult::Failed, + }; + + channel.send((name, result)).unwrap(); + }) + .unwrap(); +} + +struct TestSummary { + stdout: StandardStream, + total: usize, + filtered_out: usize, + passed: usize, + failed: Vec, +} + +impl TestSummary { + fn new(total: usize, filtered_out: usize) -> Self { + Self { + stdout: StandardStream::stdout(ColorChoice::Auto), + total, + filtered_out, + passed: 0, + failed: Vec::new(), + } + } + + fn handle_result(&mut self, name: String, result: TestResult) -> io::Result<()> { + write!(self.stdout, "test {} ... ", name)?; + match result { + TestResult::Ok => { + self.passed += 1; + self.write_ok()?; + }, + TestResult::Failed => { + self.failed.push(name); + self.write_failed()?; + }, + TestResult::FailedWithMsg(msg) => { + self.failed.push(name); + self.write_failed()?; + writeln!(self.stdout)?; + + write!(self.stdout, "Error: {}", msg)?; + }, + } + writeln!(self.stdout)?; + Ok(()) + } + + fn write_ok(&mut self) -> io::Result<()> { + self.stdout + .set_color(ColorSpec::new().set_fg(Some(Color::Green)))?; + write!(self.stdout, "ok")?; + self.stdout.reset()?; + Ok(()) + } + + fn write_failed(&mut self) -> io::Result<()> { + self.stdout + .set_color(ColorSpec::new().set_fg(Some(Color::Red)))?; + write!(self.stdout, "FAILED")?; + self.stdout.reset()?; + Ok(()) + } + + fn write_starting_msg(&mut self) -> io::Result<()> { + writeln!(self.stdout)?; + writeln!( + self.stdout, + "running {} tests", + self.total - self.filtered_out + )?; + Ok(()) + } + + fn write_summary(&mut self) -> io::Result<()> { + // Print out the failing tests + if !self.failed.is_empty() { + writeln!(self.stdout)?; + writeln!(self.stdout, "failures:")?; + for name in &self.failed { + writeln!(self.stdout, " {}", name)?; + } + } + + writeln!(self.stdout)?; + write!(self.stdout, "test result: ")?; + if self.failed.is_empty() { + self.write_ok()?; + } else { + self.write_failed()?; + } + writeln!( + self.stdout, + ". {} passed; {} failed; {} filtered out", + self.passed, + self.failed.len(), + self.filtered_out + )?; + writeln!(self.stdout)?; + Ok(()) + } + + fn success(&self) -> bool { + self.failed.is_empty() + } +} + +#[doc(hidden)] +pub struct Requirements { + test: fn(&Path) -> Result<()>, + test_name: String, + root: String, + pattern: String, +} + +impl Requirements { + #[doc(hidden)] + pub fn new( + test: fn(&Path) -> Result<()>, + test_name: String, + root: String, + pattern: String, + ) -> Self { + Self { + test, + test_name, + root, + pattern, + } + } + + /// Generate standard test descriptors ([`test::TestDescAndFn`]) from the descriptor of + /// `#[datatest::files(..)]`. + /// + /// Scans all files in a given directory, finds matching ones and generates a test descriptor + /// for each of them. + fn expand(&self) -> Vec { + let root = Path::new(&self.root).to_path_buf(); + + let re = regex::Regex::new(&self.pattern) + .unwrap_or_else(|_| panic!("invalid regular expression: '{}'", self.pattern)); + + let tests: Vec<_> = iterate_directory(&root) + .filter_map(|path| { + let input_path = path.to_string_lossy(); + if re.is_match(&input_path) { + let testfn = self.test; + let name = derive_test_name(&root, &path, &self.test_name); + let testfn = Box::new(move || (testfn)(&path)); + + Some(Test { testfn, name }) + } else { + None + } + }) + .collect(); + + // We want to avoid silent fails due to typos in regexp! + if tests.is_empty() { + panic!( + "no test cases found for test '{}'. Scanned directory: '{}' with pattern '{}'", + self.test_name, self.root, self.pattern, + ); + } + + tests + } +} diff --git a/movement-sdk/clis/aptos/src/node/analyze/analyze_validators.rs b/movement-sdk/clis/aptos/src/node/analyze/analyze_validators.rs new file mode 100644 index 000000000..a5fa20b10 --- /dev/null +++ b/movement-sdk/clis/aptos/src/node/analyze/analyze_validators.rs @@ -0,0 +1,540 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::fetch_metadata::ValidatorInfo; +use anyhow::Result; +use aptos_bitvec::BitVec; +use aptos_rest_client::VersionedNewBlockEvent; +use aptos_storage_interface::{DbReader, Order}; +use aptos_types::{ + account_address::AccountAddress, + account_config::{new_block_event_key, NewBlockEvent}, +}; +use itertools::Itertools; +use std::{cmp::Ordering, collections::HashMap, convert::TryFrom, ops::Add}; + +/// Single validator stats +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct ValidatorStats { + /// Number of successful proposals + pub proposal_successes: u32, + /// Number of failed proposals + pub proposal_failures: u32, + /// Number of votes proposals + pub votes: u32, + /// Number of transactions in a block + pub transactions: u32, + /// Voting power + pub voting_power: u64, +} + +impl ValidatorStats { + /// Proposal failure rate + pub fn failure_rate(&self) -> f32 { + (self.proposal_failures as f32) / (self.proposal_failures + self.proposal_successes) as f32 + } + + /// Whether node is proposing well enough + pub fn is_reliable(&self) -> bool { + (self.proposal_successes > 0) && (self.failure_rate() < 0.1) + } + + // Whether node is voting well enough + pub fn is_voting_enough(&self, rounds: u32) -> bool { + self.votes as f32 > rounds as f32 * 0.3 + } +} + +#[derive(Debug, Eq, PartialEq, Hash)] +pub enum NodeState { + // Proposal failure < 10%, >30% votes + Reliable, + // Proposal failure < 10%, <30% votes + ReliableLowVotes, + // Has successful proposals, but proposal failure > 10% + AliveUnreliable, + // No successful proposals, but voting + OnlyVoting, + // Not participating in consensus + NotParticipatingInConsensus, + // Not in ValidatorSet + Absent, +} + +impl NodeState { + pub fn to_char(&self) -> &str { + match self { + Self::Reliable => "+", + Self::ReliableLowVotes => "P", + Self::AliveUnreliable => "~", + Self::OnlyVoting => "V", + Self::NotParticipatingInConsensus => "X", + Self::Absent => " ", + } + } + + // Large the value, the worse the node is performing. + pub fn to_order_weight(&self) -> usize { + match self { + Self::Reliable => 0, + Self::ReliableLowVotes => 100, + Self::AliveUnreliable => 10000, + Self::OnlyVoting => 1000000, + Self::NotParticipatingInConsensus => 100000000, + Self::Absent => 1, + } + } +} + +impl Add for ValidatorStats { + type Output = Self; + + fn add(self, other: Self) -> Self { + Self { + proposal_successes: self.proposal_successes + other.proposal_successes, + proposal_failures: self.proposal_failures + other.proposal_failures, + votes: self.votes + other.votes, + transactions: self.transactions + other.transactions, + voting_power: 0, // cannot aggregate voting power. + } + } +} + +/// Statistics for all validators +#[derive(Clone)] +pub struct EpochStats { + /// Statistics for each of the validators + pub validator_stats: HashMap, + /// Total rounds in an epoch + pub total_rounds: u32, + /// Total transactions in an epoch + pub total_transactions: u32, + /// Successful rounds in an epoch + pub round_successes: u32, + /// Failed rounds in an epoch + pub round_failures: u32, + /// Nil blocks in an epoch + pub nil_blocks: u32, + /// Total voting power + pub total_voting_power: u128, +} + +impl EpochStats { + pub fn to_state(&self, validator: &AccountAddress) -> NodeState { + self.validator_stats + .get(validator) + .map(|b| { + if b.is_reliable() { + if b.is_voting_enough(self.total_rounds) { + NodeState::Reliable + } else { + NodeState::ReliableLowVotes + } + } else if b.proposal_successes > 0 { + NodeState::AliveUnreliable + } else if b.votes > 0 { + NodeState::OnlyVoting + } else { + NodeState::NotParticipatingInConsensus + } + }) + .unwrap_or(NodeState::Absent) + } + + pub fn to_votes(&self, validator: &AccountAddress) -> u32 { + self.validator_stats + .get(validator) + .map(|s| s.votes) + .unwrap_or(0) + } + + pub fn to_voting_power(&self, validator: &AccountAddress) -> u64 { + self.validator_stats + .get(validator) + .map(|s| s.voting_power) + .unwrap_or(0) + } +} + +impl Add for EpochStats { + type Output = Self; + + fn add(self, other: Self) -> Self { + let mut validator_stats = self.validator_stats; + for (key, other_validator_stats) in other.validator_stats.into_iter() { + validator_stats.insert( + key, + other_validator_stats + + *validator_stats.get(&key).unwrap_or(&ValidatorStats { + proposal_failures: 0, + proposal_successes: 0, + votes: 0, + transactions: 0, + voting_power: 0, + }), + ); + } + Self { + validator_stats, + total_rounds: self.total_rounds + other.total_rounds, + round_successes: self.round_successes + other.round_successes, + round_failures: self.round_failures + other.round_failures, + nil_blocks: self.nil_blocks + other.nil_blocks, + total_transactions: self.total_transactions + other.total_transactions, + total_voting_power: 0, + } + } +} + +/// Analyze validator performance +pub struct AnalyzeValidators {} + +impl AnalyzeValidators { + /// Fetch all events from a single epoch from DB. + pub fn fetch_epoch(epoch: u64, aptos_db: &dyn DbReader) -> Result> { + let batch = 100; + + let mut cursor = u64::max_value(); + let mut result: Vec = vec![]; + let ledger_version = aptos_db.get_latest_ledger_info()?.ledger_info().version(); + + loop { + let raw_events = aptos_db.get_events( + &new_block_event_key(), + cursor, + Order::Descending, + batch as u64, + ledger_version, + )?; + let end = raw_events.len() < batch; + for raw_event in raw_events { + if cursor <= raw_event.event.sequence_number() { + println!( + "Duplicate event found for {} : {:?}", + cursor, + raw_event.event.sequence_number() + ); + } else { + cursor = raw_event.event.sequence_number(); + let event = bcs::from_bytes::(raw_event.event.event_data())?; + + match epoch.cmp(&event.epoch()) { + Ordering::Equal => { + result.push(VersionedNewBlockEvent { + event, + version: raw_event.transaction_version, + sequence_number: raw_event.event.sequence_number(), + }); + }, + Ordering::Greater => { + return Ok(result); + }, + Ordering::Less => {}, + }; + } + } + + if end { + return Ok(result); + } + } + } + + /// Analyze single epoch + pub fn analyze(blocks: &[VersionedNewBlockEvent], validators: &[ValidatorInfo]) -> EpochStats { + assert!( + validators.iter().as_slice().windows(2).all(|w| { + w[0].validator_index + .partial_cmp(&w[1].validator_index) + .map(|o| o != Ordering::Greater) + .unwrap_or(false) + }), + "Validators need to be sorted" + ); + assert!( + blocks.iter().as_slice().windows(2).all(|w| { + w[0].event + .round() + .partial_cmp(&w[1].event.round()) + .map(|o| o != Ordering::Greater) + .unwrap_or(false) + }), + "Blocks need to be sorted" + ); + + let mut successes = HashMap::::new(); + let mut failures = HashMap::::new(); + let mut votes = HashMap::::new(); + let mut transactions = HashMap::::new(); + + let mut trimmed_rounds = 0; + let mut nil_blocks = 0; + let mut previous_round = 0; + for (pos, block) in blocks.iter().enumerate() { + let event = &block.event; + let is_nil = event.proposer() == AccountAddress::ZERO; + if is_nil { + nil_blocks += 1; + } + let expected_round = + previous_round + u64::from(!is_nil) + event.failed_proposer_indices().len() as u64; + if event.round() != expected_round { + println!( + "Missing failed AccountAddresss : {} {:?}", + previous_round, &event + ); + assert!(expected_round < event.round()); + trimmed_rounds += event.round() - expected_round; + } + previous_round = event.round(); + + if !is_nil { + *successes.entry(event.proposer()).or_insert(0) += 1; + } + + for failed_proposer_index in event.failed_proposer_indices() { + *failures + .entry(validators[*failed_proposer_index as usize].address) + .or_insert(0) += 1; + } + + let previous_block_votes_bitvec: BitVec = + event.previous_block_votes_bitvec().clone().into(); + assert_eq!( + BitVec::required_buckets(validators.len() as u16), + previous_block_votes_bitvec.num_buckets() + ); + for (i, validator) in validators.iter().enumerate() { + if previous_block_votes_bitvec.is_set(i as u16) { + *votes.entry(validator.address).or_insert(0) += 1; + } + } + + let cur_transactions_option = blocks + .get(pos + 1) + .map(|next| u32::try_from(next.version - block.version - 2).unwrap()); + if let Some(cur_transactions) = cur_transactions_option { + if is_nil { + assert_eq!( + cur_transactions, + 0, + "{} {:?}", + block.version, + blocks.get(pos + 1) + ); + } + *transactions.entry(event.proposer()).or_insert(0) += cur_transactions; + } + } + let total_successes: u32 = successes.values().sum(); + let total_failures: u32 = failures.values().sum(); + let total_transactions: u32 = transactions.values().sum(); + let total_rounds = total_successes + total_failures; + assert_eq!( + total_rounds + u32::try_from(trimmed_rounds).unwrap(), + previous_round as u32, + "{} success + {} failures + {} trimmed != {}", + total_successes, + total_failures, + trimmed_rounds, + previous_round + ); + + return EpochStats { + validator_stats: validators + .iter() + .map(|validator| { + (validator.address, ValidatorStats { + proposal_successes: *successes.get(&validator.address).unwrap_or(&0), + proposal_failures: *failures.get(&validator.address).unwrap_or(&0), + votes: *votes.get(&validator.address).unwrap_or(&0), + transactions: *transactions.get(&validator.address).unwrap_or(&0), + voting_power: validator.voting_power, + }) + }) + .collect(), + total_rounds, + total_transactions, + round_successes: total_successes, + round_failures: total_failures, + nil_blocks, + total_voting_power: validators + .iter() + .map(|validator| validator.voting_power as u128) + .sum(), + }; + } + + /// Print validator stats in a table + pub fn print_detailed_epoch_table( + epoch_stats: &EpochStats, + extra: Option<(&str, &HashMap)>, + sort_by_health: bool, + ) { + println!( + "Rounds: {} successes, {} failures, {} NIL blocks, failure rate: {}%, nil block rate: {}%", + epoch_stats.round_successes, epoch_stats.round_failures, epoch_stats.nil_blocks, + 100.0 * epoch_stats.round_failures as f32 / epoch_stats.total_rounds as f32, + 100.0 * epoch_stats.nil_blocks as f32 / epoch_stats.total_rounds as f32, + ); + println!( + "{: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <30}", + "elected", + "% rounds", + "% failed", + "succeded", + "failed", + "voted", + "transact", + extra.map(|(column, _)| column).unwrap_or("") + ); + + let mut validator_order: Vec<&AccountAddress> = + epoch_stats.validator_stats.keys().collect(); + if sort_by_health { + validator_order.sort_by_cached_key(|v| { + epoch_stats + .validator_stats + .get(v) + .map(|s| { + ( + if s.proposal_successes > 0 { + (s.failure_rate() * 100000.0) as u32 + } else { + 200000 + }, + -((s.proposal_failures + s.proposal_successes) as i32), + *v, + ) + }) + .unwrap() + }); + } else { + validator_order.sort(); + } + + for validator in validator_order { + let cur_stats = epoch_stats.validator_stats.get(validator).unwrap(); + println!( + "{: <10} | {:5.2}% | {:7.3}% | {: <10} | {: <10} | {: <10} | {: <10} | {}", + cur_stats.proposal_failures + cur_stats.proposal_successes, + 100.0 * (cur_stats.proposal_failures + cur_stats.proposal_successes) as f32 + / (epoch_stats.total_rounds as f32), + 100.0 * cur_stats.failure_rate(), + cur_stats.proposal_successes, + cur_stats.proposal_failures, + cur_stats.votes, + cur_stats.transactions, + if let Some((_, extra_map)) = extra { + format!( + "{: <30} | {}", + extra_map.get(validator).unwrap_or(&"".to_string()), + validator + ) + } else { + format!("{}", validator) + } + ); + } + } + + pub fn print_validator_health_over_time( + stats: &HashMap, + validators: &[AccountAddress], + extra: Option<&HashMap>, + ) { + let epochs: Vec<_> = stats.keys().sorted().collect(); + + let mut sorted_validators = validators.to_vec(); + sorted_validators.sort_by_cached_key(|validator| { + ( + epochs + .iter() + .map(|cur_epoch| { + stats + .get(cur_epoch) + .unwrap() + .to_state(validator) + .to_order_weight() + }) + .sum::(), + *validator, + ) + }); + + for validator in sorted_validators { + print!( + "{}: ", + if let Some(extra_map) = extra { + format!( + "{: <30} | {}", + extra_map.get(&validator).unwrap_or(&""), + validator + ) + } else { + format!("{}", validator) + } + ); + for cur_epoch in epochs.iter() { + print!( + "{}", + stats.get(cur_epoch).unwrap().to_state(&validator).to_char() + ); + } + println!(); + } + } + + pub fn print_network_health_over_time( + stats: &HashMap, + validators: &[AccountAddress], + ) { + let epochs = stats.keys().sorted(); + + println!( + "{: <8} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10}", + "epoch", + "reliable", + "r low vote", + "unreliable", + "only vote", + "down(cons)", + "rounds", + "#r failed", + "% failure", + "% stake has >10% of votes", + ); + for cur_epoch in epochs { + let epoch_stats = stats.get(cur_epoch).unwrap(); + + let counts = validators.iter().map(|v| epoch_stats.to_state(v)).counts(); + + let voted_voting_power: u128 = validators + .iter() + .flat_map(|v| { + if epoch_stats.to_votes(v) > epoch_stats.round_successes / 10 { + Some(epoch_stats.to_voting_power(v) as u128) + } else { + None + } + }) + .sum(); + + println!( + "{: <8} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {:10.2} | {:10.2}", + cur_epoch, + counts.get(&NodeState::Reliable).unwrap_or(&0), + counts.get(&NodeState::ReliableLowVotes).unwrap_or(&0), + counts.get(&NodeState::AliveUnreliable).unwrap_or(&0), + counts.get(&NodeState::OnlyVoting).unwrap_or(&0), + counts + .get(&NodeState::NotParticipatingInConsensus) + .unwrap_or(&0), + epoch_stats.total_rounds, + epoch_stats.round_failures, + 100.0 * epoch_stats.round_failures as f32 / epoch_stats.total_rounds as f32, + 100.0 * voted_voting_power as f32 / epoch_stats.total_voting_power as f32, + ); + } + } +} diff --git a/movement-sdk/clis/aptos/src/node/analyze/fetch_metadata.rs b/movement-sdk/clis/aptos/src/node/analyze/fetch_metadata.rs new file mode 100644 index 000000000..e9ab24e8b --- /dev/null +++ b/movement-sdk/clis/aptos/src/node/analyze/fetch_metadata.rs @@ -0,0 +1,337 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::{anyhow, Result}; +use aptos_rest_client::{ + aptos_api_types::{IdentifierWrapper, MoveResource, WriteSetChange}, + Client as RestClient, Transaction, VersionedNewBlockEvent, +}; +use aptos_types::account_address::AccountAddress; +use std::str::FromStr; + +const MAX_FETCH_BATCH_SIZE: u16 = 1000; + +#[derive(Eq, PartialEq, Clone, Copy, Debug)] +pub struct ValidatorInfo { + pub address: AccountAddress, + pub voting_power: u64, + pub validator_index: u16, +} + +pub struct EpochInfo { + pub epoch: u64, + pub blocks: Vec, + pub validators: Vec, + pub partial: bool, +} + +pub struct FetchMetadata {} + +impl FetchMetadata { + fn get_validator_addresses( + data: &MoveResource, + field_name: &str, + ) -> Result> { + fn extract_validator_address(validator: &serde_json::Value) -> Result { + Ok(ValidatorInfo { + address: AccountAddress::from_hex_literal( + validator.get("addr").unwrap().as_str().unwrap(), + ) + .map_err(|e| anyhow!("Cannot parse address {:?}", e))?, + voting_power: validator + .get("voting_power") + .unwrap() + .as_str() + .unwrap() + .parse() + .map_err(|e| anyhow!("Cannot parse voting_power {:?}", e))?, + validator_index: validator + .get("config") + .unwrap() + .get("validator_index") + .unwrap() + .as_str() + .unwrap() + .parse() + .map_err(|e| anyhow!("Cannot parse validator_index {:?}", e))?, + }) + } + + let validators_json = data + .data + .0 + .get(&IdentifierWrapper::from_str(field_name).unwrap()) + .unwrap(); + if let serde_json::Value::Array(validators_array) = validators_json { + let mut validators: Vec = vec![]; + for validator in validators_array { + validators.push(extract_validator_address(validator)?); + } + Ok(validators) + } else { + Err(anyhow!("{} validators not in json", field_name)) + } + } + + async fn get_transactions_in_range( + client: &RestClient, + start: u64, + last: u64, + ) -> Result> { + let mut result = Vec::new(); + let mut cursor = start; + while cursor < last { + let limit = std::cmp::min(MAX_FETCH_BATCH_SIZE as u64, last - cursor) as u16; + let mut current = client + .get_transactions(Some(cursor), Some(limit)) + .await? + .into_inner(); + if current.is_empty() { + return Err(anyhow!( + "No transactions returned with start={} and limit={}", + cursor, + limit + )); + } + cursor += current.len() as u64; + result.append(&mut current); + } + Ok(result) + } + + fn get_validators_from_transaction(transaction: &Transaction) -> Result> { + if let Ok(info) = transaction.transaction_info() { + for change in &info.changes { + if let WriteSetChange::WriteResource(resource) = change { + if resource.data.typ.name.0.as_str() == "ValidatorSet" { + // No pending at epoch change + assert_eq!( + Vec::::new(), + FetchMetadata::get_validator_addresses( + &resource.data, + "pending_inactive" + )? + ); + assert_eq!( + Vec::::new(), + FetchMetadata::get_validator_addresses( + &resource.data, + "pending_active" + )? + ); + return FetchMetadata::get_validator_addresses( + &resource.data, + "active_validators", + ); + } + } + } + } + Err(anyhow!("Couldn't find ValidatorSet in the transaction")) + } + + pub async fn fetch_new_block_events( + client: &RestClient, + start_epoch: Option, + end_epoch: Option, + ) -> Result> { + let (last_events, state) = client + .get_new_block_events_bcs(None, Some(1)) + .await? + .into_parts(); + let mut start_seq_num = state.oldest_block_height; + assert_eq!(last_events.len(), 1, "{:?}", last_events); + let last_event = last_events.first().unwrap(); + let last_seq_num = last_event.sequence_number; + + let wanted_start_epoch = { + let mut wanted_start_epoch = start_epoch.unwrap_or(2); + if wanted_start_epoch < 0 { + wanted_start_epoch = last_event.event.epoch() as i64 + wanted_start_epoch + 1; + } + + let oldest_event = client + .get_new_block_events_bcs(Some(start_seq_num), Some(1)) + .await? + .into_inner() + .into_iter() + .next() + .ok_or_else(|| anyhow!("No blocks at oldest_block_height {}", start_seq_num))?; + let oldest_fetchable_epoch = std::cmp::max(oldest_event.event.epoch() + 1, 2); + if oldest_fetchable_epoch > wanted_start_epoch as u64 { + println!( + "Oldest full epoch that can be retreived is {} ", + oldest_fetchable_epoch + ); + oldest_fetchable_epoch + } else { + wanted_start_epoch as u64 + } + }; + let wanted_end_epoch = { + let mut wanted_end_epoch = end_epoch.unwrap_or(i64::MAX); + if wanted_end_epoch < 0 { + wanted_end_epoch = last_event.event.epoch() as i64 + wanted_end_epoch + 1; + } + std::cmp::min( + last_event.event.epoch() + 1, + std::cmp::max(2, wanted_end_epoch) as u64, + ) + }; + + if wanted_start_epoch > 2 { + let mut search_end = last_seq_num; + + // Stop when search is close enough, and we can then linearly + // proceed from there. + // Since we are ignoring results we are fetching during binary search + // we want to stop when we are close. + while start_seq_num + 20 < search_end { + let mid = (start_seq_num + search_end) / 2; + + let mid_epoch = client + .get_new_block_events_bcs(Some(mid), Some(1)) + .await? + .into_inner() + .first() + .unwrap() + .event + .epoch(); + + if mid_epoch < wanted_start_epoch { + start_seq_num = mid; + } else { + search_end = mid; + } + } + } + + let mut batch_index = 0; + + println!( + "Fetching {} to {} sequence number, wanting epochs [{}, {}), last version: {} and epoch: {}", + start_seq_num, last_seq_num, wanted_start_epoch, wanted_end_epoch, state.version, state.epoch, + ); + let mut result: Vec = vec![]; + if wanted_start_epoch >= wanted_end_epoch { + return Ok(result); + } + + let mut validators: Vec = vec![]; + let mut current: Vec = vec![]; + let mut epoch = 0; + + let mut cursor = start_seq_num; + loop { + let response = client + .get_new_block_events_bcs(Some(cursor), Some(MAX_FETCH_BATCH_SIZE)) + .await; + + if response.is_err() { + println!( + "Failed to read new_block_events beyond {}, stopping. {:?}", + cursor, + response.unwrap_err() + ); + assert!(!validators.is_empty()); + result.push(EpochInfo { + epoch, + blocks: current, + validators: validators.clone(), + partial: true, + }); + return Ok(result); + } + let events = response.unwrap().into_inner(); + + if events.is_empty() { + return Err(anyhow!( + "No transactions returned with start={} and limit={}", + cursor, + MAX_FETCH_BATCH_SIZE + )); + } + + cursor += events.len() as u64; + batch_index += 1; + + for event in events { + if event.event.epoch() > epoch { + if epoch == 0 { + epoch = event.event.epoch(); + current = vec![]; + } else { + let last = current.last().cloned(); + if let Some(last) = last { + let transactions = FetchMetadata::get_transactions_in_range( + client, + last.version, + event.version, + ) + .await?; + assert_eq!( + transactions.first().unwrap().version().unwrap(), + last.version + ); + for transaction in transactions { + if let Ok(new_validators) = + FetchMetadata::get_validators_from_transaction(&transaction) + { + if epoch >= wanted_start_epoch { + assert!(!validators.is_empty()); + result.push(EpochInfo { + epoch, + blocks: current, + validators: validators.clone(), + partial: false, + }); + } + current = vec![]; + + validators = new_validators; + validators.sort_by_key(|v| v.validator_index); + assert_eq!(epoch + 1, event.event.epoch()); + epoch = event.event.epoch(); + if epoch >= wanted_end_epoch { + return Ok(result); + } + break; + } + } + assert!( + current.is_empty(), + "Couldn't find ValidatorSet change for transactions start={}, limit={} for epoch {}", + last.version, + event.version - last.version, + event.event.epoch(), + ); + } + } + } + current.push(event); + } + + if batch_index % 100 == 0 { + println!( + "Fetched {} epochs (in epoch {} with {} blocks) from {} NewBlockEvents", + result.len(), + epoch, + current.len(), + cursor + ); + } + + if cursor > last_seq_num { + if !validators.is_empty() { + result.push(EpochInfo { + epoch, + blocks: current, + validators: validators.clone(), + partial: true, + }); + } + return Ok(result); + } + } + } +} diff --git a/movement-sdk/clis/aptos/src/node/analyze/mod.rs b/movement-sdk/clis/aptos/src/node/analyze/mod.rs new file mode 100644 index 000000000..ac8c70163 --- /dev/null +++ b/movement-sdk/clis/aptos/src/node/analyze/mod.rs @@ -0,0 +1,5 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +pub mod analyze_validators; +pub mod fetch_metadata; diff --git a/movement-sdk/clis/aptos/src/node/mod.rs b/movement-sdk/clis/aptos/src/node/mod.rs new file mode 100644 index 000000000..33caaac4e --- /dev/null +++ b/movement-sdk/clis/aptos/src/node/mod.rs @@ -0,0 +1,1730 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +pub mod analyze; + +use crate::{ + common::{ + types::{ + CliCommand, CliError, CliResult, CliTypedResult, ConfigSearchMode, + OptionalPoolAddressArgs, PoolAddressArgs, ProfileOptions, PromptOptions, RestOptions, + TransactionOptions, TransactionSummary, + }, + utils::{prompt_yes_with_override, read_from_file}, + }, + config::GlobalConfig, + genesis::git::from_yaml, + node::analyze::{ + analyze_validators::{AnalyzeValidators, ValidatorStats}, + fetch_metadata::FetchMetadata, + }, +}; +use aptos_backup_cli::{ + coordinators::restore::{RestoreCoordinator, RestoreCoordinatorOpt}, + metadata::cache::MetadataCacheOpt, + storage::command_adapter::{config::CommandAdapterConfig, CommandAdapter}, + utils::{ConcurrentDownloadsOpt, GlobalRestoreOpt, ReplayConcurrencyLevelOpt, RocksdbOpt}, +}; +use aptos_cached_packages::aptos_stdlib; +use aptos_config::config::NodeConfig; +use aptos_crypto::{bls12381, bls12381::PublicKey, x25519, ValidCryptoMaterialStringExt}; +use aptos_faucet_core::server::{FunderKeyEnum, RunConfig}; +use aptos_genesis::config::{HostAndPort, OperatorConfiguration}; +use aptos_network_checker::args::{ + validate_address, CheckEndpointArgs, HandshakeArgs, NodeAddressArgs, +}; +use aptos_rest_client::{aptos_api_types::VersionedEvent, Client, State}; +use aptos_types::{ + account_address::AccountAddress, + account_config::{BlockResource, CORE_CODE_ADDRESS}, + chain_id::ChainId, + network_address::NetworkAddress, + on_chain_config::{ConfigurationResource, ConsensusScheme, ValidatorSet}, + stake_pool::StakePool, + staking_contract::StakingContractStore, + validator_info::ValidatorInfo, + validator_performances::ValidatorPerformances, + vesting::VestingAdminStore, +}; +use async_trait::async_trait; +use bcs::Result; +use chrono::{DateTime, NaiveDateTime, Utc}; +use clap::Parser; +use futures::FutureExt; +use hex::FromHex; +use rand::{rngs::StdRng, SeedableRng}; +use reqwest::Url; +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + convert::{TryFrom, TryInto}, + path::PathBuf, + pin::Pin, + sync::Arc, + thread, + time::Duration, +}; +use tokio::time::Instant; + +const SECS_TO_MICROSECS: u64 = 1_000_000; + +/// Tool for operations related to nodes +/// +/// This tool allows you to run a local test node for testing, +/// identify issues with nodes, and show related information. +#[derive(Parser)] +pub enum NodeTool { + AnalyzeValidatorPerformance(AnalyzeValidatorPerformance), + BootstrapDbFromBackup(BootstrapDbFromBackup), + CheckNetworkConnectivity(CheckNetworkConnectivity), + GetPerformance(GetPerformance), + GetStakePool(GetStakePool), + InitializeValidator(InitializeValidator), + JoinValidatorSet(JoinValidatorSet), + LeaveValidatorSet(LeaveValidatorSet), + ShowEpochInfo(ShowEpochInfo), + ShowValidatorConfig(ShowValidatorConfig), + ShowValidatorSet(ShowValidatorSet), + ShowValidatorStake(ShowValidatorStake), + RunLocalTestnet(RunLocalTestnet), + UpdateConsensusKey(UpdateConsensusKey), + UpdateValidatorNetworkAddresses(UpdateValidatorNetworkAddresses), +} + +impl NodeTool { + pub async fn execute(self) -> CliResult { + use NodeTool::*; + match self { + AnalyzeValidatorPerformance(tool) => tool.execute_serialized().await, + BootstrapDbFromBackup(tool) => tool.execute_serialized().await, + CheckNetworkConnectivity(tool) => tool.execute_serialized().await, + GetPerformance(tool) => tool.execute_serialized().await, + GetStakePool(tool) => tool.execute_serialized().await, + InitializeValidator(tool) => tool.execute_serialized().await, + JoinValidatorSet(tool) => tool.execute_serialized().await, + LeaveValidatorSet(tool) => tool.execute_serialized().await, + ShowEpochInfo(tool) => tool.execute_serialized().await, + ShowValidatorSet(tool) => tool.execute_serialized().await, + ShowValidatorStake(tool) => tool.execute_serialized().await, + ShowValidatorConfig(tool) => tool.execute_serialized().await, + RunLocalTestnet(tool) => tool.execute_serialized_without_logger().await, + UpdateConsensusKey(tool) => tool.execute_serialized().await, + UpdateValidatorNetworkAddresses(tool) => tool.execute_serialized().await, + } + } +} + +#[derive(Parser)] +pub struct OperatorConfigFileArgs { + /// Operator Configuration file + /// + /// Config file created from the `genesis set-validator-configuration` command + #[clap(long, parse(from_os_str))] + pub(crate) operator_config_file: Option, +} + +impl OperatorConfigFileArgs { + fn load(&self) -> CliTypedResult> { + if let Some(ref file) = self.operator_config_file { + Ok(from_yaml( + &String::from_utf8(read_from_file(file)?).map_err(CliError::from)?, + )?) + } else { + Ok(None) + } + } +} + +#[derive(Parser)] +pub struct ValidatorConsensusKeyArgs { + /// Hex encoded Consensus public key + /// + /// The key should be a BLS12-381 public key + #[clap(long, parse(try_from_str = bls12381::PublicKey::from_encoded_string))] + pub(crate) consensus_public_key: Option, + + /// Hex encoded Consensus proof of possession + /// + /// The key should be a BLS12-381 proof of possession + #[clap(long, parse(try_from_str = bls12381::ProofOfPossession::from_encoded_string))] + pub(crate) proof_of_possession: Option, +} + +impl ValidatorConsensusKeyArgs { + fn get_consensus_public_key<'a>( + &'a self, + operator_config: &'a Option, + ) -> CliTypedResult<&'a bls12381::PublicKey> { + let consensus_public_key = if let Some(ref consensus_public_key) = self.consensus_public_key + { + consensus_public_key + } else if let Some(ref operator_config) = operator_config { + &operator_config.consensus_public_key + } else { + return Err(CliError::CommandArgumentError( + "Must provide either --operator-config-file or --consensus-public-key".to_string(), + )); + }; + Ok(consensus_public_key) + } + + fn get_consensus_proof_of_possession<'a>( + &'a self, + operator_config: &'a Option, + ) -> CliTypedResult<&'a bls12381::ProofOfPossession> { + let proof_of_possession = if let Some(ref proof_of_possession) = self.proof_of_possession { + proof_of_possession + } else if let Some(ref operator_config) = operator_config { + &operator_config.consensus_proof_of_possession + } else { + return Err(CliError::CommandArgumentError( + "Must provide either --operator-config-file or --proof-of-possession".to_string(), + )); + }; + Ok(proof_of_possession) + } +} + +#[derive(Parser)] +pub struct ValidatorNetworkAddressesArgs { + /// Host and port pair for the validator + /// + /// e.g. 127.0.0.1:6180 + #[clap(long)] + pub(crate) validator_host: Option, + + /// Validator x25519 public network key + #[clap(long, parse(try_from_str = x25519::PublicKey::from_encoded_string))] + pub(crate) validator_network_public_key: Option, + + /// Host and port pair for the fullnode + /// + /// e.g. 127.0.0.1:6180. Optional + #[clap(long)] + pub(crate) full_node_host: Option, + + /// Full node x25519 public network key + #[clap(long, parse(try_from_str = x25519::PublicKey::from_encoded_string))] + pub(crate) full_node_network_public_key: Option, +} + +impl ValidatorNetworkAddressesArgs { + fn get_network_configs<'a>( + &'a self, + operator_config: &'a Option, + ) -> CliTypedResult<( + x25519::PublicKey, + Option, + &'a HostAndPort, + Option<&'a HostAndPort>, + )> { + let validator_network_public_key = + if let Some(public_key) = self.validator_network_public_key { + public_key + } else if let Some(ref operator_config) = operator_config { + operator_config.validator_network_public_key + } else { + return Err(CliError::CommandArgumentError( + "Must provide either --operator-config-file or --validator-network-public-key" + .to_string(), + )); + }; + + let full_node_network_public_key = + if let Some(public_key) = self.full_node_network_public_key { + Some(public_key) + } else if let Some(ref operator_config) = operator_config { + operator_config.full_node_network_public_key + } else { + None + }; + + let validator_host = if let Some(ref host) = self.validator_host { + host + } else if let Some(ref operator_config) = operator_config { + &operator_config.validator_host + } else { + return Err(CliError::CommandArgumentError( + "Must provide either --operator-config-file or --validator-host".to_string(), + )); + }; + + let full_node_host = if let Some(ref host) = self.full_node_host { + Some(host) + } else if let Some(ref operator_config) = operator_config { + operator_config.full_node_host.as_ref() + } else { + None + }; + + Ok(( + validator_network_public_key, + full_node_network_public_key, + validator_host, + full_node_host, + )) + } +} + +#[derive(Copy, Clone, Debug, Serialize)] +pub enum StakePoolType { + Direct, + StakingContract, + Vesting, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum StakePoolState { + Active, + Inactive, + PendingActive, + PendingInactive, +} + +#[derive(Debug, Serialize)] +pub struct StakePoolResult { + pub state: StakePoolState, + pub pool_address: AccountAddress, + pub operator_address: AccountAddress, + pub voter_address: AccountAddress, + pub pool_type: StakePoolType, + pub total_stake: u64, + pub commission_percentage: u64, + pub commission_not_yet_unlocked: u64, + pub lockup_expiration_utc_time: DateTime, + pub consensus_public_key: String, + pub validator_network_addresses: Vec, + pub fullnode_network_addresses: Vec, + pub epoch_info: EpochInfo, + #[serde(skip_serializing_if = "Option::is_none")] + pub vesting_contract: Option, +} + +/// Show the stake pool +/// +/// Retrieves the associated stake pool from the multiple types for the given owner address +#[derive(Parser)] +pub struct GetStakePool { + /// The owner address of the stake pool + #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] + pub(crate) owner_address: AccountAddress, + #[clap(flatten)] + pub(crate) rest_options: RestOptions, + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, +} + +#[async_trait] +impl CliCommand> for GetStakePool { + fn command_name(&self) -> &'static str { + "GetStakePool" + } + + async fn execute(mut self) -> CliTypedResult> { + let owner_address = self.owner_address; + let client = &self.rest_options.client(&self.profile_options)?; + get_stake_pools(client, owner_address).await + } +} + +#[derive(Debug, Serialize)] +pub struct StakePoolPerformance { + current_epoch_successful_proposals: u64, + current_epoch_failed_proposals: u64, + previous_epoch_rewards: Vec, + epoch_info: EpochInfo, +} + +/// Show staking performance of the given staking pool +#[derive(Parser)] +pub struct GetPerformance { + #[clap(flatten)] + pub(crate) pool_address_args: PoolAddressArgs, + #[clap(flatten)] + pub(crate) rest_options: RestOptions, + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, +} + +#[async_trait] +impl CliCommand for GetPerformance { + fn command_name(&self) -> &'static str { + "GetPerformance" + } + + async fn execute(mut self) -> CliTypedResult { + let client = &self.rest_options.client(&self.profile_options)?; + let pool_address = self.pool_address_args.pool_address; + let validator_set = &client + .get_account_resource_bcs::(CORE_CODE_ADDRESS, "0x1::stake::ValidatorSet") + .await? + .into_inner(); + + let mut current_epoch_successful_proposals = 0; + let mut current_epoch_failed_proposals = 0; + let state = get_stake_pool_state(validator_set, &pool_address); + if state == StakePoolState::Active || state == StakePoolState::PendingInactive { + let validator_config = client + .get_account_resource_bcs::( + pool_address, + "0x1::stake::ValidatorConfig", + ) + .await? + .into_inner(); + let validator_performances = &client + .get_account_resource_bcs::( + CORE_CODE_ADDRESS, + "0x1::stake::ValidatorPerformance", + ) + .await? + .into_inner(); + let validator_index = validator_config.validator_index as usize; + current_epoch_successful_proposals = + validator_performances.validators[validator_index].successful_proposals; + current_epoch_failed_proposals = + validator_performances.validators[validator_index].failed_proposals; + }; + + let previous_epoch_rewards = client + .get_account_events( + pool_address, + "0x1::stake::StakePool", + "distribute_rewards_events", + Some(0), + Some(10), + ) + .await + .unwrap() + .into_inner() + .into_iter() + .map(|e: VersionedEvent| { + e.data + .get("rewards_amount") + .unwrap() + .as_str() + .unwrap() + .into() + }) + .collect(); + + Ok(StakePoolPerformance { + current_epoch_successful_proposals, + current_epoch_failed_proposals, + previous_epoch_rewards, + epoch_info: get_epoch_info(client).await?, + }) + } +} + +/// Retrieves all stake pools associated with an account +pub async fn get_stake_pools( + client: &Client, + owner_address: AccountAddress, +) -> CliTypedResult> { + let epoch_info = get_epoch_info(client).await?; + let validator_set = &client + .get_account_resource_bcs::(CORE_CODE_ADDRESS, "0x1::stake::ValidatorSet") + .await? + .into_inner(); + let mut stake_pool_results: Vec = vec![]; + // Add direct stake pool if any. + let direct_stake_pool = get_stake_pool_info( + client, + owner_address, + StakePoolType::Direct, + 0, + 0, + epoch_info.clone(), + validator_set, + None, + ) + .await; + if let Ok(direct_stake_pool) = direct_stake_pool { + stake_pool_results.push(direct_stake_pool); + }; + + // Fetch all stake pools managed via staking contracts. + let staking_contract_pools = get_staking_contract_pools( + client, + owner_address, + StakePoolType::StakingContract, + epoch_info.clone(), + validator_set, + None, + ) + .await; + if let Ok(mut staking_contract_pools) = staking_contract_pools { + stake_pool_results.append(&mut staking_contract_pools); + }; + + // Fetch all stake pools managed via employee vesting accounts. + let vesting_admin_store = client + .get_account_resource_bcs::(owner_address, "0x1::vesting::AdminStore") + .await; + if let Ok(vesting_admin_store) = vesting_admin_store { + let vesting_contracts = vesting_admin_store.into_inner().vesting_contracts; + for vesting_contract in vesting_contracts { + let mut staking_contract_pools = get_staking_contract_pools( + client, + vesting_contract, + StakePoolType::Vesting, + epoch_info.clone(), + validator_set, + Some(vesting_contract), + ) + .await + .unwrap(); + stake_pool_results.append(&mut staking_contract_pools); + } + }; + + Ok(stake_pool_results) +} + +/// Retrieve 0x1::staking_contract related pools +pub async fn get_staking_contract_pools( + client: &Client, + staker_address: AccountAddress, + pool_type: StakePoolType, + epoch_info: EpochInfo, + validator_set: &ValidatorSet, + vesting_contract: Option, +) -> CliTypedResult> { + let mut stake_pool_results: Vec = vec![]; + let staking_contract_store = client + .get_account_resource_bcs::( + staker_address, + "0x1::staking_contract::Store", + ) + .await?; + let staking_contracts = staking_contract_store.into_inner().staking_contracts; + for staking_contract in staking_contracts { + let stake_pool_address = get_stake_pool_info( + client, + staking_contract.value.pool_address, + pool_type, + staking_contract.value.principal, + staking_contract.value.commission_percentage, + epoch_info.clone(), + validator_set, + vesting_contract, + ) + .await + .unwrap(); + stake_pool_results.push(stake_pool_address); + } + Ok(stake_pool_results) +} + +pub async fn get_stake_pool_info( + client: &Client, + pool_address: AccountAddress, + pool_type: StakePoolType, + principal: u64, + commission_percentage: u64, + epoch_info: EpochInfo, + validator_set: &ValidatorSet, + vesting_contract: Option, +) -> CliTypedResult { + let stake_pool = client + .get_account_resource_bcs::(pool_address, "0x1::stake::StakePool") + .await? + .into_inner(); + let validator_config = client + .get_account_resource_bcs::(pool_address, "0x1::stake::ValidatorConfig") + .await? + .into_inner(); + let total_stake = stake_pool.get_total_staked_amount(); + let commission_not_yet_unlocked = (total_stake - principal) * commission_percentage / 100; + let state = get_stake_pool_state(validator_set, &pool_address); + + let consensus_public_key = if validator_config.consensus_public_key.is_empty() { + "".into() + } else { + PublicKey::try_from(&validator_config.consensus_public_key[..]) + .unwrap() + .to_encoded_string() + .unwrap() + }; + Ok(StakePoolResult { + state, + pool_address, + operator_address: stake_pool.operator_address, + voter_address: stake_pool.delegated_voter, + pool_type, + total_stake, + commission_percentage, + commission_not_yet_unlocked, + lockup_expiration_utc_time: Time::new_seconds(stake_pool.locked_until_secs).utc_time, + consensus_public_key, + validator_network_addresses: validator_config + .validator_network_addresses() + .unwrap_or_default(), + fullnode_network_addresses: validator_config + .fullnode_network_addresses() + .unwrap_or_default(), + epoch_info, + vesting_contract, + }) +} + +fn get_stake_pool_state( + validator_set: &ValidatorSet, + pool_address: &AccountAddress, +) -> StakePoolState { + if validator_set.active_validators().contains(pool_address) { + StakePoolState::Active + } else if validator_set + .pending_active_validators() + .contains(pool_address) + { + StakePoolState::PendingActive + } else if validator_set + .pending_inactive_validators() + .contains(pool_address) + { + StakePoolState::PendingInactive + } else { + StakePoolState::Inactive + } +} + +/// Register the current account as a validator +/// +/// This will create a new stake pool for the given account. The voter and operator fields will be +/// defaulted to the stake pool account if not provided. +#[derive(Parser)] +pub struct InitializeValidator { + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, + #[clap(flatten)] + pub(crate) operator_config_file_args: OperatorConfigFileArgs, + #[clap(flatten)] + pub(crate) validator_consensus_key_args: ValidatorConsensusKeyArgs, + #[clap(flatten)] + pub(crate) validator_network_addresses_args: ValidatorNetworkAddressesArgs, +} + +#[async_trait] +impl CliCommand for InitializeValidator { + fn command_name(&self) -> &'static str { + "InitializeValidator" + } + + async fn execute(mut self) -> CliTypedResult { + let operator_config = self.operator_config_file_args.load()?; + let consensus_public_key = self + .validator_consensus_key_args + .get_consensus_public_key(&operator_config)?; + let consensus_proof_of_possession = self + .validator_consensus_key_args + .get_consensus_proof_of_possession(&operator_config)?; + let ( + validator_network_public_key, + full_node_network_public_key, + validator_host, + full_node_host, + ) = self + .validator_network_addresses_args + .get_network_configs(&operator_config)?; + let validator_network_addresses = + vec![validator_host.as_network_address(validator_network_public_key)?]; + let full_node_network_addresses = + match (full_node_host.as_ref(), full_node_network_public_key) { + (Some(host), Some(public_key)) => vec![host.as_network_address(public_key)?], + (None, None) => vec![], + _ => { + return Err(CliError::CommandArgumentError( + "If specifying fullnode addresses, both host and public key are required." + .to_string(), + )) + }, + }; + + self.txn_options + .submit_transaction(aptos_stdlib::stake_initialize_validator( + consensus_public_key.to_bytes().to_vec(), + consensus_proof_of_possession.to_bytes().to_vec(), + // BCS encode, so that we can hide the original type + bcs::to_bytes(&validator_network_addresses)?, + bcs::to_bytes(&full_node_network_addresses)?, + )) + .await + .map(|inner| inner.into()) + } +} + +/// Arguments used for operator of the staking pool +#[derive(Parser)] +pub struct OperatorArgs { + #[clap(flatten)] + pub(crate) pool_address_args: OptionalPoolAddressArgs, +} + +impl OperatorArgs { + fn address_fallback_to_profile( + &self, + profile_options: &ProfileOptions, + ) -> CliTypedResult { + if let Some(address) = self.pool_address_args.pool_address { + Ok(address) + } else { + profile_options.account_address() + } + } + + fn address_fallback_to_txn( + &self, + transaction_options: &TransactionOptions, + ) -> CliTypedResult { + if let Some(address) = self.pool_address_args.pool_address { + Ok(address) + } else { + transaction_options.sender_address() + } + } +} + +/// Join the validator set after meeting staking requirements +/// +/// Joining the validator set requires sufficient stake. Once the transaction +/// succeeds, you will join the validator set in the next epoch. +#[derive(Parser)] +pub struct JoinValidatorSet { + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, + #[clap(flatten)] + pub(crate) operator_args: OperatorArgs, +} + +#[async_trait] +impl CliCommand for JoinValidatorSet { + fn command_name(&self) -> &'static str { + "JoinValidatorSet" + } + + async fn execute(mut self) -> CliTypedResult { + let address = self + .operator_args + .address_fallback_to_txn(&self.txn_options)?; + + self.txn_options + .submit_transaction(aptos_stdlib::stake_join_validator_set(address)) + .await + .map(|inner| inner.into()) + } +} + +/// Leave the validator set +/// +/// Leaving the validator set will require you to have unlocked and withdrawn all stake. After this +/// transaction is successful, you will leave the validator set in the next epoch. +#[derive(Parser)] +pub struct LeaveValidatorSet { + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, + #[clap(flatten)] + pub(crate) operator_args: OperatorArgs, +} + +#[async_trait] +impl CliCommand for LeaveValidatorSet { + fn command_name(&self) -> &'static str { + "LeaveValidatorSet" + } + + async fn execute(mut self) -> CliTypedResult { + let address = self + .operator_args + .address_fallback_to_txn(&self.txn_options)?; + + self.txn_options + .submit_transaction(aptos_stdlib::stake_leave_validator_set(address)) + .await + .map(|inner| inner.into()) + } +} + +/// Show validator stake information for a specific validator +/// +/// This will show information about a specific validator, given its +/// `--pool-address`. +#[derive(Parser)] +pub struct ShowValidatorStake { + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, + #[clap(flatten)] + pub(crate) rest_options: RestOptions, + #[clap(flatten)] + pub(crate) operator_args: OperatorArgs, +} + +#[async_trait] +impl CliCommand for ShowValidatorStake { + fn command_name(&self) -> &'static str { + "ShowValidatorStake" + } + + async fn execute(mut self) -> CliTypedResult { + let client = self.rest_options.client(&self.profile_options)?; + let address = self + .operator_args + .address_fallback_to_profile(&self.profile_options)?; + let response = client + .get_resource(address, "0x1::stake::StakePool") + .await?; + Ok(response.into_inner()) + } +} + +/// Show validator configuration for a specific validator +/// +/// This will show information about a specific validator, given its +/// `--pool-address`. +#[derive(Parser)] +pub struct ShowValidatorConfig { + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, + #[clap(flatten)] + pub(crate) rest_options: RestOptions, + #[clap(flatten)] + pub(crate) operator_args: OperatorArgs, +} + +#[async_trait] +impl CliCommand for ShowValidatorConfig { + fn command_name(&self) -> &'static str { + "ShowValidatorConfig" + } + + async fn execute(mut self) -> CliTypedResult { + let client = self.rest_options.client(&self.profile_options)?; + let address = self + .operator_args + .address_fallback_to_profile(&self.profile_options)?; + let validator_config: ValidatorConfig = client + .get_account_resource_bcs(address, "0x1::stake::ValidatorConfig") + .await? + .into_inner(); + Ok((&validator_config) + .try_into() + .map_err(|err| CliError::BCS("Validator config", err))?) + } +} + +/// Show validator details of the validator set +/// +/// This will show information about the validators including their voting power, addresses, and +/// public keys. +#[derive(Parser)] +pub struct ShowValidatorSet { + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, + #[clap(flatten)] + pub(crate) rest_options: RestOptions, +} + +#[async_trait] +impl CliCommand for ShowValidatorSet { + fn command_name(&self) -> &'static str { + "ShowValidatorSet" + } + + async fn execute(mut self) -> CliTypedResult { + let client = self.rest_options.client(&self.profile_options)?; + let validator_set: ValidatorSet = client + .get_account_resource_bcs(CORE_CODE_ADDRESS, "0x1::stake::ValidatorSet") + .await? + .into_inner(); + + ValidatorSetSummary::try_from(&validator_set) + .map_err(|err| CliError::BCS("Validator Set", err)) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct ValidatorSetSummary { + pub scheme: ConsensusScheme, + pub active_validators: Vec, + pub pending_inactive: Vec, + pub pending_active: Vec, + pub total_voting_power: u128, + pub total_joining_power: u128, +} + +impl TryFrom<&ValidatorSet> for ValidatorSetSummary { + type Error = bcs::Error; + + fn try_from(set: &ValidatorSet) -> Result { + Ok(ValidatorSetSummary { + scheme: set.scheme, + active_validators: set + .active_validators + .iter() + .filter_map(|validator| validator.try_into().ok()) + .collect(), + pending_inactive: set + .pending_inactive + .iter() + .filter_map(|validator| validator.try_into().ok()) + .collect(), + pending_active: set + .pending_active + .iter() + .filter_map(|validator| validator.try_into().ok()) + .collect(), + total_voting_power: set.total_voting_power, + total_joining_power: set.total_joining_power, + }) + } +} + +impl From<&ValidatorSetSummary> for ValidatorSet { + fn from(summary: &ValidatorSetSummary) -> Self { + ValidatorSet { + scheme: summary.scheme, + active_validators: summary + .active_validators + .iter() + .map(|validator| validator.into()) + .collect(), + pending_inactive: summary + .pending_inactive + .iter() + .map(|validator| validator.into()) + .collect(), + pending_active: summary + .pending_active + .iter() + .map(|validator| validator.into()) + .collect(), + total_voting_power: summary.total_voting_power, + total_joining_power: summary.total_joining_power, + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct ValidatorInfoSummary { + // The validator's account address. AccountAddresses are initially derived from the account + // auth pubkey; however, the auth key can be rotated, so one should not rely on this + // initial property. + pub account_address: AccountAddress, + // Voting power of this validator + consensus_voting_power: u64, + // Validator config + config: ValidatorConfigSummary, +} + +impl TryFrom<&ValidatorInfo> for ValidatorInfoSummary { + type Error = bcs::Error; + + fn try_from(info: &ValidatorInfo) -> Result { + let config = info.config(); + let config = ValidatorConfig { + consensus_public_key: config.consensus_public_key.to_bytes().to_vec(), + validator_network_addresses: config.validator_network_addresses.clone(), + fullnode_network_addresses: config.fullnode_network_addresses.clone(), + validator_index: config.validator_index, + }; + Ok(ValidatorInfoSummary { + account_address: info.account_address, + consensus_voting_power: info.consensus_voting_power(), + config: ValidatorConfigSummary::try_from(&config)?, + }) + } +} + +impl From<&ValidatorInfoSummary> for ValidatorInfo { + fn from(summary: &ValidatorInfoSummary) -> Self { + let config = &summary.config; + ValidatorInfo::new( + summary.account_address, + summary.consensus_voting_power, + aptos_types::validator_config::ValidatorConfig::new( + PublicKey::from_encoded_string(&config.consensus_public_key).unwrap(), + bcs::to_bytes(&config.validator_network_addresses).unwrap(), + bcs::to_bytes(&config.fullnode_network_addresses).unwrap(), + config.validator_index, + ), + ) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +pub struct ValidatorConfig { + pub consensus_public_key: Vec, + pub validator_network_addresses: Vec, + pub fullnode_network_addresses: Vec, + pub validator_index: u64, +} + +impl ValidatorConfig { + pub fn new( + consensus_public_key: Vec, + validator_network_addresses: Vec, + fullnode_network_addresses: Vec, + validator_index: u64, + ) -> Self { + ValidatorConfig { + consensus_public_key, + validator_network_addresses, + fullnode_network_addresses, + validator_index, + } + } + + pub fn fullnode_network_addresses(&self) -> Result, bcs::Error> { + bcs::from_bytes(&self.fullnode_network_addresses) + } + + pub fn validator_network_addresses(&self) -> Result, bcs::Error> { + bcs::from_bytes(&self.validator_network_addresses) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct ValidatorConfigSummary { + pub consensus_public_key: String, + /// This is an bcs serialized `Vec` + pub validator_network_addresses: Vec, + /// This is an bcs serialized `Vec` + pub fullnode_network_addresses: Vec, + pub validator_index: u64, +} + +impl TryFrom<&ValidatorConfig> for ValidatorConfigSummary { + type Error = bcs::Error; + + fn try_from(config: &ValidatorConfig) -> Result { + let consensus_public_key = if config.consensus_public_key.is_empty() { + "".into() + } else { + PublicKey::try_from(&config.consensus_public_key[..]) + .unwrap() + .to_encoded_string() + .unwrap() + }; + Ok(ValidatorConfigSummary { + consensus_public_key, + // TODO: We should handle if some of these are not parsable + validator_network_addresses: config.validator_network_addresses()?, + fullnode_network_addresses: config.fullnode_network_addresses()?, + validator_index: config.validator_index, + }) + } +} + +impl From<&ValidatorConfigSummary> for ValidatorConfig { + fn from(summary: &ValidatorConfigSummary) -> Self { + let consensus_public_key = if summary.consensus_public_key.is_empty() { + vec![] + } else { + summary.consensus_public_key.as_bytes().to_vec() + }; + ValidatorConfig { + consensus_public_key, + validator_network_addresses: bcs::to_bytes(&summary.validator_network_addresses) + .unwrap(), + fullnode_network_addresses: bcs::to_bytes(&summary.fullnode_network_addresses).unwrap(), + validator_index: summary.validator_index, + } + } +} + +const MAX_WAIT_S: u64 = 30; +const WAIT_INTERVAL_MS: u64 = 100; +const TESTNET_FOLDER: &str = "testnet"; + +/// Run local testnet +/// +/// This local testnet will run it's own Genesis and run as a single node +/// network locally. Optionally, a faucet can be added for minting APT coins. +#[derive(Parser)] +pub struct RunLocalTestnet { + /// An overridable config template for the test node + /// + /// If provided, the config will be used, and any needed configuration for the local testnet + /// will override the config's values + #[clap(long, parse(from_os_str))] + config_path: Option, + + /// The directory to save all files for the node + /// + /// Defaults to .aptos/testnet + #[clap(long, parse(from_os_str))] + test_dir: Option, + + /// Random seed for key generation in test mode + /// + /// This allows you to have deterministic keys for testing + #[clap(long, parse(try_from_str = FromHex::from_hex))] + seed: Option<[u8; 32]>, + + /// Clean the state and start with a new chain at genesis + /// + /// This will wipe the aptosdb in `test-dir` to remove any incompatible changes, and start + /// the chain fresh. Note, that you will need to publish the module again and distribute funds + /// from the faucet accordingly + #[clap(long)] + force_restart: bool, + + /// Run a faucet alongside the node + /// + /// Allows you to run a faucet alongside the node to create and fund accounts for testing + #[clap(long)] + with_faucet: bool, + + /// Port to run the faucet on + /// + /// When running, you'll be able to use the faucet at `http://localhost:/mint` e.g. + /// `http//localhost:8080/mint` + #[clap(long, default_value = "8081")] + faucet_port: u16, + + /// Disable the delegation of faucet minting to a dedicated account + #[clap(long)] + do_not_delegate: bool, + + #[clap(flatten)] + prompt_options: PromptOptions, +} + +#[async_trait] +impl CliCommand<()> for RunLocalTestnet { + fn command_name(&self) -> &'static str { + "RunLocalTestnet" + } + + async fn execute(mut self) -> CliTypedResult<()> { + let rng = self + .seed + .map(StdRng::from_seed) + .unwrap_or_else(StdRng::from_entropy); + + let global_config = GlobalConfig::load()?; + let test_dir = match self.test_dir { + Some(test_dir) => test_dir, + None => global_config + .get_config_location(ConfigSearchMode::CurrentDirAndParents)? + .join(TESTNET_FOLDER), + }; + + // Remove the current test directory and start with a new node + if self.force_restart && test_dir.exists() { + prompt_yes_with_override( + "Are you sure you want to delete the existing chain?", + self.prompt_options, + )?; + std::fs::remove_dir_all(test_dir.as_path()).map_err(|err| { + CliError::IO(format!("Failed to delete {}", test_dir.display()), err) + })?; + } + + // Spawn the node in a separate thread + let config_path = self.config_path.clone(); + let test_dir_copy = test_dir.clone(); + let node_thread_handle = thread::spawn(move || { + let result = aptos_node::setup_test_environment_and_start_node( + config_path, + Some(test_dir_copy), + false, + false, + aptos_cached_packages::head_release_bundle(), + rng, + ); + eprintln!("Node stopped unexpectedly {:#?}", result); + }); + + // Run faucet if selected + let maybe_faucet_future = if self.with_faucet { + let max_wait = Duration::from_secs(MAX_WAIT_S); + let wait_interval = Duration::from_millis(WAIT_INTERVAL_MS); + + // Load the config to get the rest port + let config_path = test_dir.join("0").join("node.yaml"); + + // We have to wait for the node to be configured above in the other thread + let mut config = None; + let start = Instant::now(); + while start.elapsed() < max_wait { + if let Ok(loaded_config) = NodeConfig::load_from_path(&config_path) { + config = Some(loaded_config); + break; + } + tokio::time::sleep(wait_interval).await; + } + + // Retrieve the port from the local node + let port = if let Some(config) = config { + config.api.address.port() + } else { + return Err(CliError::UnexpectedError( + "Failed to find node configuration to start faucet".to_string(), + )); + }; + + // Check that the REST API is ready + let rest_url = Url::parse(&format!("http://localhost:{}", port)).map_err(|err| { + CliError::UnexpectedError(format!("Failed to parse localhost URL {}", err)) + })?; + let rest_client = aptos_rest_client::Client::new(rest_url.clone()); + let start = Instant::now(); + let mut started_successfully = false; + + while start.elapsed() < max_wait { + if rest_client.get_index().await.is_ok() { + started_successfully = true; + break; + } + tokio::time::sleep(wait_interval).await + } + + if !started_successfully { + return Err(CliError::UnexpectedError(format!( + "Local node at {} did not start up before faucet", + rest_url + ))); + } + + // Build the config for the faucet service. + let faucet_config = RunConfig::build_for_cli( + rest_url, + self.faucet_port, + FunderKeyEnum::KeyFile(test_dir.join("mint.key")), + self.do_not_delegate, + None, + ); + + // Start the faucet + Some(faucet_config.run().map(|result| { + eprintln!("Faucet stopped unexpectedly {:#?}", result); + })) + } else { + None + }; + + // Collect futures that should never end. + let mut futures: Vec + Send>>> = Vec::new(); + + // This future just waits for the node thread. + let node_future = async move { + loop { + if node_thread_handle.is_finished() { + return; + } + tokio::time::sleep(Duration::from_millis(500)).await; + } + }; + + // Wait for all the futures. We should never get past this point unless + // something goes wrong or the user signals for the process to end. + futures.push(Box::pin(node_future)); + if let Some(faucet_future) = maybe_faucet_future { + futures.push(Box::pin(faucet_future)); + } + futures::future::select_all(futures).await; + + Err(CliError::UnexpectedError( + "One of the components stopped unexpectedly".to_string(), + )) + } +} + +/// Update consensus key for the validator node +/// +/// This will take effect in the next epoch +#[derive(Parser)] +pub struct UpdateConsensusKey { + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, + #[clap(flatten)] + pub(crate) operator_args: OperatorArgs, + #[clap(flatten)] + pub(crate) operator_config_file_args: OperatorConfigFileArgs, + #[clap(flatten)] + pub(crate) validator_consensus_key_args: ValidatorConsensusKeyArgs, +} + +#[async_trait] +impl CliCommand for UpdateConsensusKey { + fn command_name(&self) -> &'static str { + "UpdateConsensusKey" + } + + async fn execute(mut self) -> CliTypedResult { + let address = self + .operator_args + .address_fallback_to_txn(&self.txn_options)?; + + let operator_config = self.operator_config_file_args.load()?; + let consensus_public_key = self + .validator_consensus_key_args + .get_consensus_public_key(&operator_config)?; + let consensus_proof_of_possession = self + .validator_consensus_key_args + .get_consensus_proof_of_possession(&operator_config)?; + self.txn_options + .submit_transaction(aptos_stdlib::stake_rotate_consensus_key( + address, + consensus_public_key.to_bytes().to_vec(), + consensus_proof_of_possession.to_bytes().to_vec(), + )) + .await + .map(|inner| inner.into()) + } +} + +/// Update the current validator's network and fullnode network addresses +/// +/// This will take effect in the next epoch +#[derive(Parser)] +pub struct UpdateValidatorNetworkAddresses { + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, + #[clap(flatten)] + pub(crate) operator_args: OperatorArgs, + #[clap(flatten)] + pub(crate) operator_config_file_args: OperatorConfigFileArgs, + #[clap(flatten)] + pub(crate) validator_network_addresses_args: ValidatorNetworkAddressesArgs, +} + +#[async_trait] +impl CliCommand for UpdateValidatorNetworkAddresses { + fn command_name(&self) -> &'static str { + "UpdateValidatorNetworkAddresses" + } + + async fn execute(mut self) -> CliTypedResult { + let address = self + .operator_args + .address_fallback_to_txn(&self.txn_options)?; + + let validator_config = self.operator_config_file_args.load()?; + let ( + validator_network_public_key, + full_node_network_public_key, + validator_host, + full_node_host, + ) = self + .validator_network_addresses_args + .get_network_configs(&validator_config)?; + let validator_network_addresses = + vec![validator_host.as_network_address(validator_network_public_key)?]; + let full_node_network_addresses = + match (full_node_host.as_ref(), full_node_network_public_key) { + (Some(host), Some(public_key)) => vec![host.as_network_address(public_key)?], + (None, None) => vec![], + _ => { + return Err(CliError::CommandArgumentError( + "If specifying fullnode addresses, both host and public key are required." + .to_string(), + )) + }, + }; + + self.txn_options + .submit_transaction(aptos_stdlib::stake_update_network_and_fullnode_addresses( + address, + // BCS encode, so that we can hide the original type + bcs::to_bytes(&validator_network_addresses)?, + bcs::to_bytes(&full_node_network_addresses)?, + )) + .await + .map(|inner| inner.into()) + } +} + +/// Analyze the performance of one or more validators +#[derive(Parser)] +pub struct AnalyzeValidatorPerformance { + /// First epoch to analyze + /// + /// Defaults to the first epoch + #[clap(long, default_value = "-2")] + pub start_epoch: i64, + + /// Last epoch to analyze + /// + /// Defaults to the latest epoch + #[clap(long)] + pub end_epoch: Option, + + /// Analyze mode for the validator: [All, DetailedEpochTable, ValidatorHealthOverTime, NetworkHealthOverTime] + #[clap(arg_enum, long)] + pub(crate) analyze_mode: AnalyzeMode, + + /// Filter of stake pool addresses to analyze + /// + /// Defaults to all stake pool addresses + #[clap(long, multiple_values = true, parse(try_from_str=crate::common::types::load_account_arg))] + pub pool_addresses: Vec, + + #[clap(flatten)] + pub(crate) rest_options: RestOptions, + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, +} + +#[derive(PartialEq, Eq, clap::ArgEnum, Clone)] +pub enum AnalyzeMode { + /// Print all other modes simultaneously + All, + /// For each epoch, print a detailed table containing performance + /// of each of the validators. + DetailedEpochTable, + /// For each validator, summarize it's performance in an epoch into + /// one of the predefined reliability buckets, + /// and prints it's performance across epochs. + ValidatorHealthOverTime, + /// For each epoch summarize how many validators were in + /// each of the reliability buckets. + NetworkHealthOverTime, +} + +#[async_trait] +impl CliCommand<()> for AnalyzeValidatorPerformance { + fn command_name(&self) -> &'static str { + "AnalyzeValidatorPerformance" + } + + async fn execute(mut self) -> CliTypedResult<()> { + let client = self.rest_options.client(&self.profile_options)?; + + let epochs = + FetchMetadata::fetch_new_block_events(&client, Some(self.start_epoch), self.end_epoch) + .await?; + let mut stats = HashMap::new(); + + let print_detailed = self.analyze_mode == AnalyzeMode::DetailedEpochTable + || self.analyze_mode == AnalyzeMode::All; + for epoch_info in epochs { + let mut epoch_stats = + AnalyzeValidators::analyze(&epoch_info.blocks, &epoch_info.validators); + if !self.pool_addresses.is_empty() { + let mut filtered_stats: HashMap = HashMap::new(); + for pool_address in &self.pool_addresses { + filtered_stats.insert( + *pool_address, + *epoch_stats.validator_stats.get(pool_address).unwrap(), + ); + } + epoch_stats.validator_stats = filtered_stats; + } + if print_detailed { + println!( + "Detailed table for {}epoch {}:", + if epoch_info.partial { "partial " } else { "" }, + epoch_info.epoch + ); + AnalyzeValidators::print_detailed_epoch_table( + &epoch_stats, + Some(( + "voting_power", + &epoch_info + .validators + .iter() + .map(|v| (v.address, v.voting_power.to_string())) + .collect::>(), + )), + true, + ); + } + if !epoch_info.partial { + stats.insert(epoch_info.epoch, epoch_stats); + } + } + + if stats.is_empty() { + println!("No data found for given input"); + return Ok(()); + } + let total_stats = stats.values().cloned().reduce(|a, b| a + b).unwrap(); + if print_detailed { + println!( + "Detailed table for all epochs [{}, {}]:", + stats.keys().min().unwrap(), + stats.keys().max().unwrap() + ); + AnalyzeValidators::print_detailed_epoch_table(&total_stats, None, true); + } + let all_validators: Vec<_> = total_stats.validator_stats.keys().cloned().collect(); + if self.analyze_mode == AnalyzeMode::ValidatorHealthOverTime + || self.analyze_mode == AnalyzeMode::All + { + println!( + "Validator health over epochs [{}, {}]:", + stats.keys().min().unwrap(), + stats.keys().max().unwrap() + ); + AnalyzeValidators::print_validator_health_over_time(&stats, &all_validators, None); + } + if self.analyze_mode == AnalyzeMode::NetworkHealthOverTime + || self.analyze_mode == AnalyzeMode::All + { + println!( + "Network health over epochs [{}, {}]:", + stats.keys().min().unwrap(), + stats.keys().max().unwrap() + ); + AnalyzeValidators::print_network_health_over_time(&stats, &all_validators); + } + Ok(()) + } +} + +/// Bootstrap AptosDB from a backup +/// +/// Enables users to load from a backup to catch their node's DB up to a known state. +#[derive(Parser)] +pub struct BootstrapDbFromBackup { + /// Config file for the source backup + /// + /// This file configures if we should use local files or cloud storage, and how to access + /// the backup. + #[clap(long, parse(from_os_str))] + config_path: PathBuf, + + /// Target database directory + /// + /// The directory to create the AptosDB with snapshots and transactions from the backup. + /// The data folder can later be used to start an Aptos node. e.g. /opt/aptos/data/db + #[clap(long = "target-db-dir", parse(from_os_str))] + pub db_dir: PathBuf, + + #[clap(flatten)] + pub metadata_cache_opt: MetadataCacheOpt, + + #[clap(flatten)] + pub concurrent_downloads: ConcurrentDownloadsOpt, + + #[clap(flatten)] + pub replay_concurrency_level: ReplayConcurrencyLevelOpt, +} + +#[async_trait] +impl CliCommand<()> for BootstrapDbFromBackup { + fn command_name(&self) -> &'static str { + "BootstrapDbFromBackup" + } + + async fn execute(self) -> CliTypedResult<()> { + let opt = RestoreCoordinatorOpt { + metadata_cache_opt: self.metadata_cache_opt, + replay_all: false, + ledger_history_start_version: None, + skip_epoch_endings: false, + }; + let global_opt = GlobalRestoreOpt { + dry_run: false, + db_dir: Some(self.db_dir), + target_version: None, + trusted_waypoints: Default::default(), + rocksdb_opt: RocksdbOpt::default(), + concurrent_downloads: self.concurrent_downloads, + replay_concurrency_level: self.replay_concurrency_level, + } + .try_into()?; + let storage = Arc::new(CommandAdapter::new( + CommandAdapterConfig::load_from_file(&self.config_path).await?, + )); + + // hack: get around this error, related to use of `async_trait`: + // error: higher-ranked lifetime error + // ... + // = note: could not prove for<'r, 's> Pin>>>: CoerceUnsized> + std::marker::Send + 's)>>> + tokio::task::spawn_blocking(|| { + let runtime = tokio::runtime::Runtime::new().unwrap(); + runtime.block_on(RestoreCoordinator::new(opt, global_opt, storage).run()) + }) + .await + .unwrap()?; + Ok(()) + } +} + +/// Checks the network connectivity of a node +/// +/// Checks network connectivity by dialing the node and attempting +/// to establish a connection with a noise handshake. +#[derive(Parser)] +pub struct CheckNetworkConnectivity { + /// `NetworkAddress` of remote server interface. + /// Examples include: + /// - `/dns/example.com/tcp/6180/noise-ik//handshake/1` + /// - `/ip4//tcp/6182/noise-ik//handshake/0` + #[clap(long, value_parser = validate_address)] + pub address: NetworkAddress, + + /// `ChainId` of remote server. + /// Examples include: + /// - Chain numbers, e.g., `2`, `3` and `25`. + /// - Chain names, e.g., `devnet`, `testnet`, `mainnet` and `testing` (for local test networks). + #[clap(long)] + pub chain_id: ChainId, + + #[clap(flatten)] + pub handshake_args: HandshakeArgs, +} + +#[async_trait] +impl CliCommand for CheckNetworkConnectivity { + fn command_name(&self) -> &'static str { + "CheckNetworkConnectivity" + } + + async fn execute(self) -> CliTypedResult { + // Create the check endpoint args for the checker + let node_address_args = NodeAddressArgs { + address: self.address, + chain_id: self.chain_id, + }; + let check_endpoint_args = CheckEndpointArgs { + node_address_args, + handshake_args: self.handshake_args, + }; + + // Check the endpoint + aptos_network_checker::check_endpoint(&check_endpoint_args, None) + .await + .map_err(|error| CliError::UnexpectedError(error.to_string())) + } +} + +/// Show epoch information +/// +/// Displays the current epoch, the epoch length, and the estimated time of the next epoch +#[derive(Parser)] +pub struct ShowEpochInfo { + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, + #[clap(flatten)] + pub(crate) rest_options: RestOptions, +} + +#[async_trait] +impl CliCommand for ShowEpochInfo { + fn command_name(&self) -> &'static str { + "ShowEpochInfo" + } + + async fn execute(self) -> CliTypedResult { + let client = &self.rest_options.client(&self.profile_options)?; + get_epoch_info(client).await + } +} + +async fn get_epoch_info(client: &Client) -> CliTypedResult { + let (block_resource, state): (BlockResource, State) = client + .get_account_resource_bcs(CORE_CODE_ADDRESS, "0x1::block::BlockResource") + .await? + .into_parts(); + let reconfig_resource: ConfigurationResource = client + .get_account_resource_at_version_bcs( + CORE_CODE_ADDRESS, + "0x1::reconfiguration::Configuration", + state.version, + ) + .await? + .into_inner(); + + let epoch_interval = block_resource.epoch_interval(); + let epoch_interval_secs = epoch_interval / SECS_TO_MICROSECS; + let last_reconfig = reconfig_resource.last_reconfiguration_time(); + Ok(EpochInfo { + epoch: reconfig_resource.epoch(), + epoch_interval_secs, + current_epoch_start_time: Time::new_micros(last_reconfig), + next_epoch_start_time: Time::new_micros(last_reconfig + epoch_interval), + }) +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EpochInfo { + epoch: u64, + epoch_interval_secs: u64, + current_epoch_start_time: Time, + next_epoch_start_time: Time, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Time { + unix_time: u128, + utc_time: DateTime, +} + +impl Time { + pub fn new(time: Duration) -> Self { + let date_time = + NaiveDateTime::from_timestamp_opt(time.as_secs() as i64, time.subsec_nanos()).unwrap(); + let utc_time = DateTime::from_utc(date_time, Utc); + // TODO: Allow configurable time zone + Self { + unix_time: time.as_micros(), + utc_time, + } + } + + pub fn new_micros(microseconds: u64) -> Self { + Self::new(Duration::from_micros(microseconds)) + } + + pub fn new_seconds(seconds: u64) -> Self { + Self::new(Duration::from_secs(seconds)) + } +} + +#[cfg(test)] +mod tests { + use crate::{CliResult, Tool}; + use clap::Parser; + + // TODO: there have to be cleaner ways to test things. Maybe a CLI test framework? + + #[tokio::test] + // Verifies basic properties about the network connectivity checker + async fn test_check_network_connectivity() { + // Verify the help function works + let args = &["movement", "node", "check-network-connectivity", "--help"]; + let help_message = run_tool_with_args(args).await.unwrap_err(); + assert_contains(help_message, "USAGE:"); // We expect the command to return USAGE info + + // Verify that an invalid address will return an error + let args = &[ + "movement", + "node", + "check-network-connectivity", + "--address", + "invalid-address", + "--chain-id", + "mainnet", + ]; + let error_message = run_tool_with_args(args).await.unwrap_err(); + assert_contains(error_message, "Invalid address"); + + // Verify that an invalid chain-id will return an error + let args = &["movement", "node", "check-network-connectivity", "--address", "/ip4/34.70.116.169/tcp/6182/noise-ik/0x249f3301db104705652e0a0c471b46d13172b2baf14e31f007413f3baee46b0c/handshake/0", "--chain-id", "invalid-chain"]; + let error_message = run_tool_with_args(args).await.unwrap_err(); + assert_contains(error_message, "Invalid value"); + + // Verify that a failure to connect will return a timeout + let args = &["movement", "node", "check-network-connectivity", "--address", "/ip4/31.71.116.169/tcp/0001/noise-ik/0x249f3301db104705652e0a0c471b46d13172b2baf14e31f007413f3baee46b0c/handshake/0", "--chain-id", "testnet"]; + let error_message = run_tool_with_args(args).await.unwrap_err(); + assert_contains(error_message, "Timed out while checking endpoint"); + } + + async fn run_tool_with_args(args: &[&str]) -> CliResult { + let tool: Tool = Tool::try_parse_from(args).map_err(|msg| msg.to_string())?; + tool.execute().await + } + + fn assert_contains(message: String, expected_string: &str) { + if !message.contains(expected_string) { + panic!( + "Expected message to contain {:?}, but it did not! Message: {:?}", + expected_string, message + ); + } + } +} diff --git a/movement-sdk/clis/aptos/src/op/key.rs b/movement-sdk/clis/aptos/src/op/key.rs new file mode 100644 index 000000000..aac2d31f7 --- /dev/null +++ b/movement-sdk/clis/aptos/src/op/key.rs @@ -0,0 +1,396 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{ + types::{ + account_address_from_public_key, CliError, CliTypedResult, EncodingOptions, + EncodingType, KeyType, RngArgs, SaveFile, + }, + utils::{ + append_file_extension, check_if_file_exists, generate_vanity_account_ed25519, + write_to_file, + }, + }, + CliCommand, CliResult, +}; +use aptos_config::config::{Peer, PeerRole}; +use aptos_crypto::{bls12381, ed25519, x25519, PrivateKey, ValidCryptoMaterial}; +use aptos_genesis::config::HostAndPort; +use aptos_types::account_address::{ + create_multisig_account_address, from_identity_public_key, AccountAddress, +}; +use async_trait::async_trait; +use clap::{Parser, Subcommand}; +use std::{ + collections::{HashMap, HashSet}, + path::{Path, PathBuf}, +}; + +pub const PUBLIC_KEY_EXTENSION: &str = "pub"; + +/// Tool for generating, inspecting, and interacting with keys +/// +/// This tool allows users to generate and extract related information +/// with all key types used on the Aptos blockchain. +#[derive(Debug, Subcommand)] +pub enum KeyTool { + Generate(GenerateKey), + ExtractPeer(ExtractPeer), +} + +impl KeyTool { + pub async fn execute(self) -> CliResult { + match self { + KeyTool::Generate(tool) => tool.execute_serialized().await, + KeyTool::ExtractPeer(tool) => tool.execute_serialized().await, + } + } +} + +/// Extract full peer information for an upstream peer +/// +/// This command builds a YAML blob that can be copied into a user's network configuration. +/// A host is required to build the network address used for the connection, and the +/// network key is required to identify the peer. +/// +/// A `private-network-key` or `public-network-key` can be given encoded on the command line, or +/// a `private-network-key-file` or a `public-network-key-file` can be given to read from. +/// The `output-file` will be a YAML serialized peer information for use in network config. +#[derive(Debug, Parser)] +pub struct ExtractPeer { + /// Host and port of the full node + /// + /// e.g. 127.0.0.1:6180 or my-awesome-dns.com:6180 + #[clap(long)] + pub(crate) host: HostAndPort, + + #[clap(flatten)] + pub(crate) network_key_input_options: NetworkKeyInputOptions, + #[clap(flatten)] + pub(crate) output_file_options: SaveFile, + #[clap(flatten)] + pub(crate) encoding_options: EncodingOptions, +} + +#[async_trait] +impl CliCommand> for ExtractPeer { + fn command_name(&self) -> &'static str { + "ExtractPeer" + } + + async fn execute(self) -> CliTypedResult> { + // Load key based on public or private + let public_key = self + .network_key_input_options + .extract_public_network_key(self.encoding_options.encoding)?; + + // Check output file exists + self.output_file_options.check_file()?; + + // Build peer info + let peer_id = from_identity_public_key(public_key); + let mut public_keys = HashSet::new(); + public_keys.insert(public_key); + + let address = self.host.as_network_address(public_key).map_err(|err| { + CliError::UnexpectedError(format!("Failed to build network address: {}", err)) + })?; + + let peer = Peer::new(vec![address], public_keys, PeerRole::Upstream); + + let mut map = HashMap::new(); + map.insert(peer_id, peer); + + // Save to file + let yaml = serde_yaml::to_string(&map) + .map_err(|err| CliError::UnexpectedError(err.to_string()))?; + self.output_file_options + .save_to_file("Extracted peer", yaml.as_bytes())?; + Ok(map) + } +} + +#[derive(Debug, Default, Parser)] +pub struct NetworkKeyInputOptions { + /// x25519 Private key input file name + #[clap(long, group = "network_key_input", parse(from_os_str))] + private_network_key_file: Option, + + /// x25519 Private key encoded in a type as shown in `encoding` + #[clap(long, group = "network_key_input")] + private_network_key: Option, + + /// x25519 Public key input file name + #[clap(long, group = "network_key_input", parse(from_os_str))] + public_network_key_file: Option, + + /// x25519 Public key encoded in a type as shown in `encoding` + #[clap(long, group = "network_key_input")] + public_network_key: Option, +} + +impl NetworkKeyInputOptions { + pub fn from_private_key_file(file: PathBuf) -> Self { + Self { + private_network_key_file: Some(file), + private_network_key: None, + public_network_key_file: None, + public_network_key: None, + } + } + + pub fn extract_public_network_key( + self, + encoding: EncodingType, + ) -> CliTypedResult { + // The grouping above prevents there from being more than one, but just in case + match (self.public_network_key, self.public_network_key_file, self.private_network_key, self.private_network_key_file){ + (Some(public_network_key), None, None, None) => encoding.decode_key("--public-network-key", public_network_key.as_bytes().to_vec()), + (None, Some(public_network_key_file),None, None) => encoding.load_key("--public-network-key-file", public_network_key_file.as_path()), + (None, None, Some(private_network_key), None) => { + let private_network_key: x25519::PrivateKey = encoding.decode_key("--private-network-key", private_network_key.as_bytes().to_vec())?; + Ok(private_network_key.public_key()) + }, + (None, None, None, Some(private_network_key_file)) => { + let private_network_key: x25519::PrivateKey = encoding.load_key("--private-network-key-file", private_network_key_file.as_path())?; + Ok(private_network_key.public_key()) + }, + _ => Err(CliError::CommandArgumentError("Must provide exactly one of [--public-network-key, --public-network-key-file, --private-network-key, --private-network-key-file]".to_string())) + } + } +} + +/// Generates a `x25519` or `ed25519` key. +/// +/// This can be used for generating an identity. Two files will be created +/// `output_file` and `output_file.pub`. `output_file` will contain the private +/// key encoded with the `encoding` and `output_file.pub` will contain the public +/// key encoded with the `encoding`. +#[derive(Debug, Parser)] +pub struct GenerateKey { + /// Key type to generate. Must be one of [x25519, ed25519, bls12381] + #[clap(long, default_value_t = KeyType::Ed25519)] + pub(crate) key_type: KeyType, + /// Vanity prefix that resultant account address should start with, e.g. 0xaceface or d00d. Each + /// additional character multiplies by a factor of 16 the computational difficulty associated + /// with generating an address, so try out shorter prefixes first and be prepared to wait for + /// longer ones + #[clap(long)] + pub vanity_prefix: Option, + /// Use this flag when vanity prefix is for a multisig account. This mines a private key for + /// a single signer account that can, as its first transaction, create a multisig account with + /// the given vanity prefix + #[clap(long)] + pub vanity_multisig: bool, + #[clap(flatten)] + pub rng_args: RngArgs, + #[clap(flatten)] + pub(crate) save_params: SaveKey, +} + +#[async_trait] +impl CliCommand> for GenerateKey { + fn command_name(&self) -> &'static str { + "GenerateKey" + } + + async fn execute(self) -> CliTypedResult> { + if self.vanity_prefix.is_some() && !matches!(self.key_type, KeyType::Ed25519) { + return Err(CliError::CommandArgumentError(format!( + "Vanity prefixes are only accepted for {} keys", + KeyType::Ed25519 + ))); + } + if self.vanity_multisig && self.vanity_prefix.is_none() { + return Err(CliError::CommandArgumentError( + "No vanity prefix provided".to_string(), + )); + } + self.save_params.check_key_file()?; + let mut keygen = self.rng_args.key_generator()?; + match self.key_type { + KeyType::X25519 => { + let private_key = keygen.generate_x25519_private_key().map_err(|err| { + CliError::UnexpectedError(format!( + "Failed to convert ed25519 to x25519 {:?}", + err + )) + })?; + self.save_params.save_key(&private_key, "x25519") + }, + KeyType::Ed25519 => { + // If no vanity prefix specified, generate a standard Ed25519 private key. + let private_key = if self.vanity_prefix.is_none() { + keygen.generate_ed25519_private_key() + } else { + // If a vanity prefix is specified, generate vanity Ed25519 account from it. + generate_vanity_account_ed25519( + self.vanity_prefix.clone().unwrap().as_str(), + self.vanity_multisig, + )? + }; + // Store CLI result from key save operation, to append vanity address(es) if needed. + let mut result_map = self.save_params.save_key(&private_key, "ed25519").unwrap(); + if self.vanity_prefix.is_some() { + let account_address = account_address_from_public_key( + &ed25519::Ed25519PublicKey::from(&private_key), + ); + // Store account address in a PathBuf so it can be displayed in CLI result. + result_map.insert( + "Account Address:", + PathBuf::from(account_address.to_hex_literal()), + ); + if self.vanity_multisig { + let multisig_account_address = + create_multisig_account_address(account_address, 0); + result_map.insert( + "Multisig Account Address:", + PathBuf::from(multisig_account_address.to_hex_literal()), + ); + } + } + return Ok(result_map); + }, + KeyType::Bls12381 => { + let private_key = keygen.generate_bls12381_private_key(); + self.save_params.save_bls_key(&private_key, "bls12381") + }, + } + } +} + +impl GenerateKey { + /// A test friendly typed key generation for x25519 keys. + pub async fn generate_x25519( + encoding: EncodingType, + key_file: &Path, + ) -> CliTypedResult<(x25519::PrivateKey, x25519::PublicKey)> { + let args = format!( + "generate --key-type {key_type:?} --output-file {key_file} --encoding {encoding:?} --assume-yes", + key_type = KeyType::X25519, + key_file = key_file.display(), + encoding = encoding, + ); + let command = GenerateKey::parse_from(args.split_whitespace()); + command.execute().await?; + Ok(( + encoding.load_key("private_key", key_file)?, + encoding.load_key( + "public_key", + &append_file_extension(key_file, PUBLIC_KEY_EXTENSION)?, + )?, + )) + } + + /// A test friendly typed key generation for e25519 keys. + pub async fn generate_ed25519( + encoding: EncodingType, + key_file: &Path, + ) -> CliTypedResult<(ed25519::Ed25519PrivateKey, ed25519::Ed25519PublicKey)> { + let args = format!( + "generate --key-type {key_type:?} --output-file {key_file} --encoding {encoding:?} --assume-yes", + key_type = KeyType::Ed25519, + key_file = key_file.display(), + encoding = encoding, + ); + let command = GenerateKey::parse_from(args.split_whitespace()); + command.execute().await?; + Ok(( + encoding.load_key("private_key", key_file)?, + encoding.load_key( + "public_key", + &append_file_extension(key_file, PUBLIC_KEY_EXTENSION)?, + )?, + )) + } +} + +#[derive(Debug, Parser)] +pub struct SaveKey { + #[clap(flatten)] + pub(crate) file_options: SaveFile, + #[clap(flatten)] + pub(crate) encoding_options: EncodingOptions, +} + +impl SaveKey { + /// Public key file name + fn public_key_file(&self) -> CliTypedResult { + append_file_extension( + self.file_options.output_file.as_path(), + PUBLIC_KEY_EXTENSION, + ) + } + + /// Public key file name + fn proof_of_possession_file(&self) -> CliTypedResult { + append_file_extension(self.file_options.output_file.as_path(), "pop") + } + + /// Check if the key file exists already + pub fn check_key_file(&self) -> CliTypedResult<()> { + // Check if file already exists + self.file_options.check_file()?; + check_if_file_exists(&self.public_key_file()?, self.file_options.prompt_options) + } + + /// Saves a key to a file encoded in a string + pub fn save_key( + self, + key: &Key, + key_name: &'static str, + ) -> CliTypedResult> { + let encoded_private_key = self.encoding_options.encoding.encode_key(key_name, key)?; + let encoded_public_key = self + .encoding_options + .encoding + .encode_key(key_name, &key.public_key())?; + + // Write private and public keys to files + let public_key_file = self.public_key_file()?; + self.file_options + .save_to_file_confidential(key_name, &encoded_private_key)?; + write_to_file(&public_key_file, key_name, &encoded_public_key)?; + + let mut map = HashMap::new(); + map.insert("PrivateKey Path", self.file_options.output_file); + map.insert("PublicKey Path", public_key_file); + Ok(map) + } + + /// Saves a key to a file encoded in a string + pub fn save_bls_key( + self, + key: &bls12381::PrivateKey, + key_name: &'static str, + ) -> CliTypedResult> { + let encoded_private_key = self.encoding_options.encoding.encode_key(key_name, key)?; + let encoded_public_key = self + .encoding_options + .encoding + .encode_key(key_name, &key.public_key())?; + let encoded_proof_of_posession = self + .encoding_options + .encoding + .encode_key(key_name, &bls12381::ProofOfPossession::create(key))?; + + // Write private and public keys to files + let public_key_file = self.public_key_file()?; + let proof_of_possession_file = self.proof_of_possession_file()?; + self.file_options + .save_to_file_confidential(key_name, &encoded_private_key)?; + write_to_file(&public_key_file, key_name, &encoded_public_key)?; + write_to_file( + &proof_of_possession_file, + key_name, + &encoded_proof_of_posession, + )?; + + let mut map = HashMap::new(); + map.insert("PrivateKey Path", self.file_options.output_file); + map.insert("PublicKey Path", public_key_file); + map.insert("Proof of possession Path", proof_of_possession_file); + Ok(map) + } +} diff --git a/movement-sdk/clis/aptos/src/op/mod.rs b/movement-sdk/clis/aptos/src/op/mod.rs new file mode 100644 index 000000000..989a2bb8e --- /dev/null +++ b/movement-sdk/clis/aptos/src/op/mod.rs @@ -0,0 +1,4 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +pub mod key; diff --git a/movement-sdk/clis/aptos/src/stake/mod.rs b/movement-sdk/clis/aptos/src/stake/mod.rs new file mode 100644 index 000000000..45168a706 --- /dev/null +++ b/movement-sdk/clis/aptos/src/stake/mod.rs @@ -0,0 +1,668 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{ + types::{ + CliCommand, CliError, CliResult, CliTypedResult, TransactionOptions, TransactionSummary, + }, + utils::prompt_yes_with_override, + }, + node::{get_stake_pools, StakePoolType}, +}; +use aptos_cached_packages::aptos_stdlib; +use aptos_types::{ + account_address::{ + create_vesting_contract_address, default_stake_pool_address, AccountAddress, + }, + vesting::VestingAdminStore, +}; +use async_trait::async_trait; +use clap::Parser; + +/// Tool for manipulating stake and stake pools +/// +#[derive(Parser)] +pub enum StakeTool { + AddStake(AddStake), + CreateStakingContract(CreateStakingContract), + DistributeVestedCoins(DistributeVestedCoins), + IncreaseLockup(IncreaseLockup), + InitializeStakeOwner(InitializeStakeOwner), + RequestCommission(RequestCommission), + SetDelegatedVoter(SetDelegatedVoter), + SetOperator(SetOperator), + UnlockStake(UnlockStake), + UnlockVestedCoins(UnlockVestedCoins), + WithdrawStake(WithdrawStake), +} + +impl StakeTool { + pub async fn execute(self) -> CliResult { + use StakeTool::*; + match self { + AddStake(tool) => tool.execute_serialized().await, + CreateStakingContract(tool) => tool.execute_serialized().await, + DistributeVestedCoins(tool) => tool.execute_serialized().await, + IncreaseLockup(tool) => tool.execute_serialized().await, + InitializeStakeOwner(tool) => tool.execute_serialized().await, + RequestCommission(tool) => tool.execute_serialized().await, + SetDelegatedVoter(tool) => tool.execute_serialized().await, + SetOperator(tool) => tool.execute_serialized().await, + UnlockStake(tool) => tool.execute_serialized().await, + UnlockVestedCoins(tool) => tool.execute_serialized().await, + WithdrawStake(tool) => tool.execute_serialized().await, + } + } +} + +/// Add APT to a stake pool +/// +/// This command allows stake pool owners to add APT to their stake. +#[derive(Parser)] +pub struct AddStake { + /// Amount of Octas (10^-8 APT) to add to stake + #[clap(long)] + pub amount: u64, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand> for AddStake { + fn command_name(&self) -> &'static str { + "AddStake" + } + + async fn execute(mut self) -> CliTypedResult> { + let client = self + .txn_options + .rest_options + .client(&self.txn_options.profile_options)?; + let amount = self.amount; + let owner_address = self.txn_options.sender_address()?; + let mut transaction_summaries: Vec = vec![]; + + let stake_pool_results = get_stake_pools(&client, owner_address).await?; + for stake_pool in stake_pool_results { + match stake_pool.pool_type { + StakePoolType::Direct => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::stake_add_stake(amount)) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::StakingContract => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::staking_contract_add_stake( + stake_pool.operator_address, + amount, + )) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::Vesting => { + return Err(CliError::UnexpectedError( + "Adding stake is not supported for vesting contracts".into(), + )) + }, + } + } + Ok(transaction_summaries) + } +} + +/// Unlock staked APT in a stake pool +/// +/// APT coins can only be unlocked if they no longer have an applied lockup period +#[derive(Parser)] +pub struct UnlockStake { + /// Amount of Octas (10^-8 APT) to unlock + #[clap(long)] + pub amount: u64, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand> for UnlockStake { + fn command_name(&self) -> &'static str { + "UnlockStake" + } + + async fn execute(mut self) -> CliTypedResult> { + let client = self + .txn_options + .rest_options + .client(&self.txn_options.profile_options)?; + let amount = self.amount; + let owner_address = self.txn_options.sender_address()?; + let mut transaction_summaries: Vec = vec![]; + + let stake_pool_results = get_stake_pools(&client, owner_address).await?; + for stake_pool in stake_pool_results { + match stake_pool.pool_type { + StakePoolType::Direct => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::stake_unlock(amount)) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::StakingContract => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::staking_contract_unlock_stake( + stake_pool.operator_address, + amount, + )) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::Vesting => { + return Err(CliError::UnexpectedError( + "Unlocking stake is not supported for vesting contracts".into(), + )) + }, + } + } + Ok(transaction_summaries) + } +} + +/// Withdraw unlocked staked APT from a stake pool +/// +/// This allows users to withdraw stake back into their CoinStore. +/// Before calling `WithdrawStake`, `UnlockStake` must be called first. +#[derive(Parser)] +pub struct WithdrawStake { + /// Amount of Octas (10^-8 APT) to withdraw. + /// This only applies to stake pools owned directly by the owner account, instead of via + /// a staking contract. In the latter case, when withdrawal is issued, all coins are distributed + #[clap(long)] + pub amount: u64, + + #[clap(flatten)] + pub(crate) node_op_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand> for WithdrawStake { + fn command_name(&self) -> &'static str { + "WithdrawStake" + } + + async fn execute(mut self) -> CliTypedResult> { + let client = self + .node_op_options + .rest_options + .client(&self.node_op_options.profile_options)?; + let amount = self.amount; + let owner_address = self.node_op_options.sender_address()?; + let mut transaction_summaries: Vec = vec![]; + + let stake_pool_results = get_stake_pools(&client, owner_address).await?; + for stake_pool in stake_pool_results { + match stake_pool.pool_type { + StakePoolType::Direct => { + transaction_summaries.push( + self.node_op_options + .submit_transaction(aptos_stdlib::stake_withdraw(amount)) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::StakingContract => { + transaction_summaries.push( + self.node_op_options + .submit_transaction(aptos_stdlib::staking_contract_distribute( + owner_address, + stake_pool.operator_address, + )) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::Vesting => { + return Err(CliError::UnexpectedError( + "Stake withdrawal from vesting contract should use distribute-vested-coins" + .into(), + )) + }, + } + } + Ok(transaction_summaries) + } +} + +/// Increase lockup of all staked APT in a stake pool +/// +/// Lockup may need to be increased in order to vote on a proposal. +#[derive(Parser)] +pub struct IncreaseLockup { + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand> for IncreaseLockup { + fn command_name(&self) -> &'static str { + "IncreaseLockup" + } + + async fn execute(mut self) -> CliTypedResult> { + let client = self + .txn_options + .rest_options + .client(&self.txn_options.profile_options)?; + let owner_address = self.txn_options.sender_address()?; + let mut transaction_summaries: Vec = vec![]; + + let stake_pool_results = get_stake_pools(&client, owner_address).await?; + for stake_pool in stake_pool_results { + match stake_pool.pool_type { + StakePoolType::Direct => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::stake_increase_lockup()) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::StakingContract => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::staking_contract_reset_lockup( + stake_pool.operator_address, + )) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::Vesting => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::vesting_reset_lockup( + stake_pool.vesting_contract.unwrap(), + )) + .await + .map(|inner| inner.into())?, + ); + }, + } + } + Ok(transaction_summaries) + } +} + +/// Initialize a stake pool owner +/// +/// Initializing stake owner adds the capability to delegate the +/// stake pool to an operator, or delegate voting to a different account. +#[derive(Parser)] +pub struct InitializeStakeOwner { + /// Initial amount of Octas (10^-8 APT) to be staked + #[clap(long)] + pub initial_stake_amount: u64, + + /// Account Address of delegated operator + #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] + pub operator_address: Option, + + /// Account address of delegated voter + #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] + pub voter_address: Option, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for InitializeStakeOwner { + fn command_name(&self) -> &'static str { + "InitializeStakeOwner" + } + + async fn execute(mut self) -> CliTypedResult { + let owner_address = self.txn_options.sender_address()?; + self.txn_options + .submit_transaction(aptos_stdlib::stake_initialize_stake_owner( + self.initial_stake_amount, + self.operator_address.unwrap_or(owner_address), + self.voter_address.unwrap_or(owner_address), + )) + .await + .map(|inner| inner.into()) + } +} + +/// Delegate operator capability to another account +/// +/// This changes teh operator capability from its current operator to a different operator. +/// By default, the operator of a stake pool is the owner of the stake pool +#[derive(Parser)] +pub struct SetOperator { + /// Account Address of delegated operator + /// + /// If not specified, it will be the same as the owner + #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] + pub operator_address: AccountAddress, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand> for SetOperator { + fn command_name(&self) -> &'static str { + "SetOperator" + } + + async fn execute(mut self) -> CliTypedResult> { + let client = self + .txn_options + .rest_options + .client(&self.txn_options.profile_options)?; + let owner_address = self.txn_options.sender_address()?; + let new_operator_address = self.operator_address; + let mut transaction_summaries: Vec = vec![]; + + let stake_pool_results = get_stake_pools(&client, owner_address).await?; + for stake_pool in stake_pool_results { + match stake_pool.pool_type { + StakePoolType::Direct => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::stake_set_operator( + new_operator_address, + )) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::StakingContract => { + transaction_summaries.push( + self.txn_options + .submit_transaction( + aptos_stdlib::staking_contract_switch_operator_with_same_commission( + stake_pool.operator_address, + new_operator_address, + ), + ) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::Vesting => { + transaction_summaries.push( + self.txn_options + .submit_transaction( + aptos_stdlib::vesting_update_operator_with_same_commission( + stake_pool.vesting_contract.unwrap(), + new_operator_address, + ), + ) + .await + .map(|inner| inner.into())?, + ); + }, + } + } + Ok(transaction_summaries) + } +} + +/// Delegate voting capability to another account +/// +/// Delegates voting capability from its current voter to a different voter. +/// By default, the voter of a stake pool is the owner of the stake pool +#[derive(Parser)] +pub struct SetDelegatedVoter { + /// Account Address of delegated voter + /// + /// If not specified, it will be the same as the owner + #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] + pub voter_address: AccountAddress, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand> for SetDelegatedVoter { + fn command_name(&self) -> &'static str { + "SetDelegatedVoter" + } + + async fn execute(mut self) -> CliTypedResult> { + let client = self + .txn_options + .rest_options + .client(&self.txn_options.profile_options)?; + let owner_address = self.txn_options.sender_address()?; + let new_voter_address = self.voter_address; + let mut transaction_summaries: Vec = vec![]; + + let stake_pool_results = get_stake_pools(&client, owner_address).await?; + for stake_pool in stake_pool_results { + match stake_pool.pool_type { + StakePoolType::Direct => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::stake_set_delegated_voter( + new_voter_address, + )) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::StakingContract => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::staking_contract_update_voter( + stake_pool.operator_address, + new_voter_address, + )) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::Vesting => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::vesting_update_voter( + stake_pool.vesting_contract.unwrap(), + new_voter_address, + )) + .await + .map(|inner| inner.into())?, + ); + }, + } + } + Ok(transaction_summaries) + } +} + +/// Create a staking contract stake pool +/// +/// +#[derive(Parser)] +pub struct CreateStakingContract { + /// Account Address of operator + #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] + pub operator: AccountAddress, + + /// Account Address of delegated voter + #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] + pub voter: AccountAddress, + + /// Amount to create the staking contract with + #[clap(long)] + pub amount: u64, + + /// Percentage of accumulated rewards to pay the operator as commission + #[clap(long)] + pub commission_percentage: u64, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for CreateStakingContract { + fn command_name(&self) -> &'static str { + "CreateStakingContract" + } + + async fn execute(mut self) -> CliTypedResult { + let pool_address = default_stake_pool_address( + self.txn_options.profile_options.account_address()?, + self.operator, + ); + prompt_yes_with_override( + &format!( + "Creating a new staking contract with pool address 0x{}. Confirm?", + pool_address + ), + self.txn_options.prompt_options, + )?; + + self.txn_options + .submit_transaction(aptos_stdlib::staking_contract_create_staking_contract( + self.operator, + self.voter, + self.amount, + self.commission_percentage, + vec![], + )) + .await + .map(|inner| inner.into()) + } +} + +/// Distribute fully unlocked coins from vesting +/// +/// Distribute fully unlocked coins (rewards and/or vested coins) from the vesting contract +/// to shareholders. +#[derive(Parser)] +pub struct DistributeVestedCoins { + /// Address of the vesting contract's admin. + #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] + pub admin_address: AccountAddress, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for DistributeVestedCoins { + fn command_name(&self) -> &'static str { + "DistributeVestedCoins" + } + + async fn execute(mut self) -> CliTypedResult { + let vesting_contract_address = create_vesting_contract_address(self.admin_address, 0, &[]); + self.txn_options + .submit_transaction(aptos_stdlib::vesting_distribute(vesting_contract_address)) + .await + .map(|inner| inner.into()) + } +} + +/// Unlock vested coins +/// +/// Unlock vested coins according to the vesting contract's schedule. +/// This also unlocks any accumulated staking rewards and pays commission to the operator of the +/// vesting contract's stake pool first. +/// +/// The unlocked vested tokens and staking rewards are still subject to the staking lockup and +/// cannot be withdrawn until after the lockup expires. +#[derive(Parser)] +pub struct UnlockVestedCoins { + /// Address of the vesting contract's admin. + #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] + pub admin_address: AccountAddress, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for UnlockVestedCoins { + fn command_name(&self) -> &'static str { + "UnlockVestedCoins" + } + + async fn execute(mut self) -> CliTypedResult { + let vesting_contract_address = create_vesting_contract_address(self.admin_address, 0, &[]); + self.txn_options + .submit_transaction(aptos_stdlib::vesting_vest(vesting_contract_address)) + .await + .map(|inner| inner.into()) + } +} + +/// Request commission from running a stake pool +/// +/// Allows operators or owners to request commission from running a stake pool (only if there's a +/// staking contract set up with the staker). The commission will be withdrawable at the end of the +/// stake pool's current lockup period. +#[derive(Parser)] +pub struct RequestCommission { + /// Address of the owner of the stake pool + #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] + pub owner_address: AccountAddress, + + /// Address of the operator of the stake pool + #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] + pub operator_address: AccountAddress, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for RequestCommission { + fn command_name(&self) -> &'static str { + "RequestCommission" + } + + async fn execute(mut self) -> CliTypedResult { + let client = self + .txn_options + .rest_options + .client(&self.txn_options.profile_options)?; + + // If this is a vesting stake pool, retrieve the associated vesting contract + let vesting_admin_store = client + .get_account_resource_bcs::( + self.owner_address, + "0x1::vesting::AdminStore", + ) + .await; + + // Note: this only works if the vesting contract has exactly one staking contract + // associated + let staker_address = if let Ok(vesting_admin_store) = vesting_admin_store { + vesting_admin_store.into_inner().vesting_contracts[0] + } else { + self.owner_address + }; + self.txn_options + .submit_transaction(aptos_stdlib::staking_contract_request_commission( + staker_address, + self.operator_address, + )) + .await + .map(|inner| inner.into()) + } +} diff --git a/movement-sdk/clis/aptos/src/test/mod.rs b/movement-sdk/clis/aptos/src/test/mod.rs new file mode 100644 index 000000000..aec14d4a7 --- /dev/null +++ b/movement-sdk/clis/aptos/src/test/mod.rs @@ -0,0 +1,1221 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + account::{ + create::{CreateAccount, DEFAULT_FUNDED_COINS}, + fund::FundWithFaucet, + key_rotation::{LookupAddress, RotateKey, RotateSummary}, + list::{ListAccount, ListQuery}, + transfer::{TransferCoins, TransferSummary}, + }, + common::{ + init::{InitTool, Network}, + types::{ + account_address_from_public_key, AccountAddressWrapper, ArgWithTypeVec, CliError, + CliTypedResult, EncodingOptions, EntryFunctionArguments, FaucetOptions, GasOptions, + KeyType, MoveManifestAccountWrapper, MovePackageDir, OptionalPoolAddressArgs, + PoolAddressArgs, PrivateKeyInputOptions, PromptOptions, PublicKeyInputOptions, + RestOptions, RngArgs, SaveFile, TransactionOptions, TransactionSummary, + }, + utils::write_to_file, + }, + governance::{ + CompileScriptFunction, ProposalSubmissionSummary, SubmitProposal, SubmitVote, + VerifyProposal, VerifyProposalResponse, + }, + move_tool::{ + ArgWithType, CompilePackage, DownloadPackage, FrameworkPackageArgs, IncludedArtifacts, + IncludedArtifactsArgs, InitPackage, MemberId, PublishPackage, RunFunction, RunScript, + TestPackage, + }, + node::{ + AnalyzeMode, AnalyzeValidatorPerformance, GetStakePool, InitializeValidator, + JoinValidatorSet, LeaveValidatorSet, OperatorArgs, OperatorConfigFileArgs, + ShowValidatorConfig, ShowValidatorSet, ShowValidatorStake, StakePoolResult, + UpdateConsensusKey, UpdateValidatorNetworkAddresses, ValidatorConfig, + ValidatorConsensusKeyArgs, ValidatorNetworkAddressesArgs, + }, + op::key::{ExtractPeer, GenerateKey, NetworkKeyInputOptions, SaveKey}, + stake::{ + AddStake, IncreaseLockup, InitializeStakeOwner, SetDelegatedVoter, SetOperator, + UnlockStake, WithdrawStake, + }, + CliCommand, +}; +use aptos_config::config::Peer; +use aptos_crypto::{ + bls12381, + ed25519::{Ed25519PrivateKey, Ed25519PublicKey}, + x25519, PrivateKey, +}; +use aptos_genesis::config::HostAndPort; +use aptos_keygen::KeyGen; +use aptos_logger::warn; +use aptos_rest_client::{ + aptos_api_types::{MoveStructTag, MoveType}, + Transaction, +}; +use aptos_sdk::move_types::{account_address::AccountAddress, language_storage::ModuleId}; +use aptos_temppath::TempPath; +use aptos_types::on_chain_config::ValidatorSet; +use move_core_types::ident_str; +use reqwest::Url; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::{ + collections::{BTreeMap, HashMap}, + mem, + path::PathBuf, + str::FromStr, + time::Duration, +}; +use tempfile::TempDir; +use thiserror::__private::AsDisplay; +#[cfg(feature = "cli-framework-test-move")] +use thiserror::__private::AsDisplay; +use tokio::time::{sleep, Instant}; + +#[cfg(test)] +mod tests; + +pub const INVALID_ACCOUNT: &str = "0xDEADBEEFCAFEBABE"; + +pub const FIRST_MOVE_FILE: &str = " +module NamedAddress0::store { + use std::string; + use aptos_framework::coin::{Self}; + + struct CoolCoin has key {} + + public entry fun init( + account: &signer, + decimals: u64, + monitor_supply: bool + ) { + let (_, _) = coin::initialize(account, string::utf8(b\"CoolCoin\"), string::utf8(b\"COOL\"), decimals, monitor_supply); + coin::register(account); + } +}"; + +/// A framework for testing the CLI +pub struct CliTestFramework { + account_addresses: Vec, + account_keys: Vec, + endpoint: Url, + faucet_endpoint: Url, + move_dir: Option, +} + +impl CliTestFramework { + pub fn local_new(num_accounts: usize) -> CliTestFramework { + let dummy_url = Url::parse("http://localhost").unwrap(); + let mut framework = CliTestFramework { + account_addresses: Vec::new(), + account_keys: Vec::new(), + endpoint: dummy_url.clone(), + faucet_endpoint: dummy_url, + move_dir: None, + }; + let mut keygen = KeyGen::from_seed([0; 32]); + for _ in 0..num_accounts { + let key = keygen.generate_ed25519_private_key(); + framework.add_account_to_cli(key); + } + framework + } + + pub async fn new(endpoint: Url, faucet_endpoint: Url, num_accounts: usize) -> CliTestFramework { + let mut framework = CliTestFramework { + account_addresses: Vec::new(), + account_keys: Vec::new(), + endpoint, + faucet_endpoint, + move_dir: None, + }; + let mut keygen = KeyGen::from_seed([0; 32]); + + for _ in 0..num_accounts { + framework + .create_cli_account_from_faucet(keygen.generate_ed25519_private_key(), None) + .await + .unwrap(); + } + + framework + } + + pub fn addresses(&self) -> Vec { + self.account_addresses.clone() + } + + async fn check_account_exists(&self, index: usize) -> bool { + // Create account if it doesn't exist (and there's a faucet) + let client = aptos_rest_client::Client::new(self.endpoint.clone()); + let address = self.account_id(index); + client.get_account(address).await.is_ok() + } + + pub fn add_account_to_cli(&mut self, private_key: Ed25519PrivateKey) -> usize { + let address = account_address_from_public_key(&private_key.public_key()); + self.account_addresses.push(address); + self.account_keys.push(private_key); + println!( + "Account: {} (index: {})", + address.to_hex_literal(), + self.account_keys.len() - 1 + ); + self.account_keys.len() - 1 + } + + pub fn add_account_with_address_to_cli( + &mut self, + private_key: Ed25519PrivateKey, + address: AccountAddress, + ) -> usize { + self.account_addresses.push(address); + self.account_keys.push(private_key); + self.account_keys.len() - 1 + } + + pub async fn create_cli_account( + &mut self, + private_key: Ed25519PrivateKey, + sender_index: usize, + ) -> CliTypedResult { + let index = self.add_account_to_cli(private_key); + if self.check_account_exists(index).await { + return Err(CliError::UnexpectedError( + "Account already exists".to_string(), + )); + } + CreateAccount { + txn_options: self.transaction_options(sender_index, None), + account: self.account_id(index), + } + .execute() + .await?; + + Ok(index) + } + + pub async fn create_cli_account_from_faucet( + &mut self, + private_key: Ed25519PrivateKey, + amount: Option, + ) -> CliTypedResult { + let index = self.add_account_to_cli(private_key); + if self.check_account_exists(index).await { + return Err(CliError::UnexpectedError( + "Account already exists".to_string(), + )); + } + + self.fund_account(index, amount).await?; + warn!( + "Funded account {:?} with {:?} OCTA", + self.account_id(index), + amount.unwrap_or(DEFAULT_FUNDED_COINS) + ); + Ok(index) + } + + pub async fn fund_account(&self, index: usize, amount: Option) -> CliTypedResult { + FundWithFaucet { + profile_options: Default::default(), + account: self.account_id(index), + faucet_options: self.faucet_options(), + amount: amount.unwrap_or(DEFAULT_FUNDED_COINS), + rest_options: self.rest_options(), + } + .execute() + .await + } + + pub async fn lookup_address( + &self, + public_key: &Ed25519PublicKey, + ) -> CliTypedResult { + LookupAddress { + public_key_options: PublicKeyInputOptions::from_key(public_key), + rest_options: self.rest_options(), + encoding_options: Default::default(), + profile_options: Default::default(), + } + .execute() + .await + } + + pub async fn rotate_key( + &mut self, + index: usize, + new_private_key: String, + gas_options: Option, + ) -> CliTypedResult { + let response = RotateKey { + txn_options: TransactionOptions { + private_key_options: PrivateKeyInputOptions::from_private_key( + self.private_key(index), + ) + .unwrap(), + sender_account: Some(self.account_id(index)), + rest_options: self.rest_options(), + gas_options: gas_options.unwrap_or_default(), + prompt_options: PromptOptions::yes(), + ..Default::default() + }, + new_private_key: Some(new_private_key), + save_to_profile: None, + new_private_key_file: None, + skip_saving_profile: true, + } + .execute() + .await?; + + Ok(response) + } + + pub async fn list_account(&self, index: usize, query: ListQuery) -> CliTypedResult> { + ListAccount { + rest_options: self.rest_options(), + profile_options: Default::default(), + account: Some(self.account_id(index)), + query, + } + .execute() + .await + } + + pub async fn transfer_coins( + &self, + sender_index: usize, + receiver_index: usize, + amount: u64, + gas_options: Option, + ) -> CliTypedResult { + TransferCoins { + txn_options: self.transaction_options(sender_index, gas_options), + account: self.account_id(receiver_index), + amount, + } + .execute() + .await + } + + pub async fn transfer_invalid_addr( + &self, + sender_index: usize, + amount: u64, + gas_options: Option, + ) -> CliTypedResult { + RunFunction { + entry_function_args: EntryFunctionArguments { + function_id: MemberId { + module_id: ModuleId::new(AccountAddress::ONE, ident_str!("coin").into()), + member_id: ident_str!("transfer").into(), + }, + arg_vec: ArgWithTypeVec { + args: vec![ + ArgWithType::from_str("address:0xdeadbeefcafebabe").unwrap(), + ArgWithType::from_str(&format!("u64:{}", amount)).unwrap(), + ], + }, + type_args: vec![MoveType::Struct(MoveStructTag::new( + AccountAddress::ONE.into(), + ident_str!("aptos_coin").into(), + ident_str!("AptosCoin").into(), + vec![], + ))], + }, + txn_options: self.transaction_options(sender_index, gas_options), + } + .execute() + .await + } + + pub async fn show_validator_config( + &self, + pool_index: usize, + ) -> CliTypedResult { + ShowValidatorConfig { + rest_options: self.rest_options(), + profile_options: Default::default(), + operator_args: self.operator_args(Some(pool_index)), + } + .execute() + .await + .map(|v| (&v).into()) + } + + pub async fn show_validator_set(&self) -> CliTypedResult { + ShowValidatorSet { + rest_options: self.rest_options(), + profile_options: Default::default(), + } + .execute() + .await + .map(|v| (&v).into()) + } + + pub async fn show_validator_stake(&self, pool_index: usize) -> CliTypedResult { + ShowValidatorStake { + rest_options: self.rest_options(), + profile_options: Default::default(), + operator_args: self.operator_args(Some(pool_index)), + } + .execute() + .await + } + + pub async fn initialize_validator( + &self, + index: usize, + consensus_public_key: bls12381::PublicKey, + proof_of_possession: bls12381::ProofOfPossession, + validator_host: HostAndPort, + validator_network_public_key: x25519::PublicKey, + ) -> CliTypedResult { + InitializeValidator { + txn_options: self.transaction_options(index, None), + operator_config_file_args: OperatorConfigFileArgs { + operator_config_file: None, + }, + validator_consensus_key_args: ValidatorConsensusKeyArgs { + consensus_public_key: Some(consensus_public_key), + proof_of_possession: Some(proof_of_possession), + }, + validator_network_addresses_args: ValidatorNetworkAddressesArgs { + validator_host: Some(validator_host), + validator_network_public_key: Some(validator_network_public_key), + full_node_host: None, + full_node_network_public_key: None, + }, + } + .execute() + .await + } + + pub async fn add_stake( + &self, + index: usize, + amount: u64, + ) -> CliTypedResult> { + AddStake { + txn_options: self.transaction_options(index, None), + amount, + } + .execute() + .await + } + + pub async fn unlock_stake( + &self, + index: usize, + amount: u64, + ) -> CliTypedResult> { + UnlockStake { + txn_options: self.transaction_options(index, None), + amount, + } + .execute() + .await + } + + pub async fn withdraw_stake( + &self, + index: usize, + amount: u64, + ) -> CliTypedResult> { + WithdrawStake { + node_op_options: self.transaction_options(index, None), + amount, + } + .execute() + .await + } + + pub async fn increase_lockup(&self, index: usize) -> CliTypedResult> { + IncreaseLockup { + txn_options: self.transaction_options(index, None), + } + .execute() + .await + } + + pub async fn join_validator_set( + &self, + operator_index: usize, + pool_index: Option, + ) -> CliTypedResult { + JoinValidatorSet { + txn_options: self.transaction_options(operator_index, None), + operator_args: self.operator_args(pool_index), + } + .execute() + .await + } + + pub async fn leave_validator_set( + &self, + operator_index: usize, + pool_index: Option, + ) -> CliTypedResult { + LeaveValidatorSet { + txn_options: self.transaction_options(operator_index, None), + operator_args: self.operator_args(pool_index), + } + .execute() + .await + } + + pub async fn update_validator_network_addresses( + &self, + operator_index: usize, + pool_index: Option, + validator_host: HostAndPort, + validator_network_public_key: x25519::PublicKey, + ) -> CliTypedResult { + UpdateValidatorNetworkAddresses { + txn_options: self.transaction_options(operator_index, None), + operator_args: self.operator_args(pool_index), + operator_config_file_args: OperatorConfigFileArgs { + operator_config_file: None, + }, + validator_network_addresses_args: ValidatorNetworkAddressesArgs { + validator_host: Some(validator_host), + validator_network_public_key: Some(validator_network_public_key), + full_node_host: None, + full_node_network_public_key: None, + }, + } + .execute() + .await + } + + pub async fn analyze_validator_performance( + &self, + start_epoch: Option, + end_epoch: Option, + ) -> CliTypedResult<()> { + AnalyzeValidatorPerformance { + start_epoch: start_epoch.unwrap_or(-2), + end_epoch, + rest_options: self.rest_options(), + profile_options: Default::default(), + analyze_mode: AnalyzeMode::All, + pool_addresses: vec![], + } + .execute() + .await + } + + pub async fn update_consensus_key( + &self, + operator_index: usize, + pool_index: Option, + consensus_public_key: bls12381::PublicKey, + proof_of_possession: bls12381::ProofOfPossession, + ) -> CliTypedResult { + UpdateConsensusKey { + txn_options: self.transaction_options(operator_index, None), + operator_args: self.operator_args(pool_index), + operator_config_file_args: OperatorConfigFileArgs { + operator_config_file: None, + }, + validator_consensus_key_args: ValidatorConsensusKeyArgs { + consensus_public_key: Some(consensus_public_key), + proof_of_possession: Some(proof_of_possession), + }, + } + .execute() + .await + } + + pub async fn init(&self, private_key: &Ed25519PrivateKey) -> CliTypedResult<()> { + InitTool { + network: Some(Network::Custom), + rest_url: Some(self.endpoint.clone()), + faucet_url: Some(self.faucet_endpoint.clone()), + rng_args: RngArgs::from_seed([0; 32]), + private_key_options: PrivateKeyInputOptions::from_private_key(private_key)?, + profile_options: Default::default(), + prompt_options: PromptOptions::yes(), + encoding_options: EncodingOptions::default(), + skip_faucet: false, + } + .execute() + .await + } + + pub async fn get_pool_address( + &self, + owner_index: usize, + ) -> CliTypedResult> { + GetStakePool { + owner_address: self.account_id(owner_index), + rest_options: self.rest_options(), + profile_options: Default::default(), + } + .execute() + .await + } + + pub async fn initialize_stake_owner( + &self, + owner_index: usize, + initial_stake_amount: u64, + voter_index: Option, + operator_index: Option, + ) -> CliTypedResult { + InitializeStakeOwner { + txn_options: self.transaction_options(owner_index, None), + initial_stake_amount, + operator_address: operator_index.map(|idx| self.account_id(idx)), + voter_address: voter_index.map(|idx| self.account_id(idx)), + } + .execute() + .await + } + + pub async fn create_stake_pool( + &self, + owner_index: usize, + operator_index: usize, + voter_index: usize, + amount: u64, + commission_percentage: u64, + ) -> CliTypedResult { + RunFunction { + entry_function_args: EntryFunctionArguments { + function_id: MemberId::from_str("0x1::staking_contract::create_staking_contract") + .unwrap(), + arg_vec: ArgWithTypeVec { + args: vec![ + ArgWithType::address(self.account_id(operator_index)), + ArgWithType::address(self.account_id(voter_index)), + ArgWithType::u64(amount), + ArgWithType::u64(commission_percentage), + ArgWithType::bytes(vec![]), + ], + }, + type_args: vec![], + }, + txn_options: self.transaction_options(owner_index, None), + } + .execute() + .await + } + + pub async fn set_operator( + &self, + owner_index: usize, + operator_index: usize, + ) -> CliTypedResult> { + SetOperator { + txn_options: self.transaction_options(owner_index, None), + operator_address: self.account_id(operator_index), + } + .execute() + .await + } + + pub async fn set_delegated_voter( + &self, + owner_index: usize, + voter_index: usize, + ) -> CliTypedResult> { + SetDelegatedVoter { + txn_options: self.transaction_options(owner_index, None), + voter_address: self.account_id(voter_index), + } + .execute() + .await + } + + /// Wait for an account to exist + pub async fn wait_for_account(&self, index: usize) -> CliTypedResult> { + let mut result = self.list_account(index, ListQuery::Balance).await; + let start = Instant::now(); + while start.elapsed() < Duration::from_secs(10) { + match result { + Ok(_) => return result, + _ => { + sleep(Duration::from_millis(500)).await; + result = self.list_account(index, ListQuery::Balance).await; + }, + }; + } + + result + } + + pub async fn account_balance_now(&self, index: usize) -> CliTypedResult { + let result = self.list_account(index, ListQuery::Balance).await?; + Ok(json_account_to_balance(result.first().unwrap())) + } + + pub async fn assert_account_balance_now(&self, index: usize, expected: u64) { + let result = self.list_account(index, ListQuery::Balance).await; + assert!( + result.is_ok(), + "Account {} not yet created, {}, last 10 transactions: {}", + self.account_id(index), + result.unwrap_err(), + self.last_n_transactions_details(10).await + ); + let accounts = result.unwrap(); + let account = accounts.first().unwrap(); + let coin = json_account_to_balance(account); + assert_eq!( + coin, + expected, + "Account {} with state: {:?}, last 10 transactions: {}", + self.account_id(index), + account, + self.last_n_transactions_details(10).await + ); + } + + async fn last_n_transactions_details(&self, count: u16) -> String { + let result = aptos_rest_client::Client::new(self.endpoint.clone()) + .get_transactions(None, Some(count)) + .await; + if let Err(e) = result { + return format!("Err({:?})", e); + } + let lines = result + .unwrap() + .inner() + .iter() + .map(|t| { + if let Transaction::UserTransaction(u) = t { + format!( + " * [{}] {}: sender={}, payload={:?}", + t.version().unwrap_or(0), + t.vm_status(), + u.request.sender, + u.request.payload + ) + } else { + format!( + " * [{}] {}: {}", + t.version().unwrap_or(0), + t.vm_status(), + t.type_str() + ) + } + }) + .collect::>(); + format!("\n{}\n", lines.join("\n")) + } + + pub async fn generate_x25519_key( + &self, + output_file: PathBuf, + seed: [u8; 32], + ) -> CliTypedResult> { + GenerateKey { + key_type: KeyType::X25519, + rng_args: RngArgs::from_seed(seed), + save_params: SaveKey { + file_options: SaveFile { + output_file, + prompt_options: PromptOptions::yes(), + }, + encoding_options: Default::default(), + }, + vanity_prefix: None, + vanity_multisig: false, + } + .execute() + .await + } + + pub async fn extract_peer( + &self, + host: HostAndPort, + private_key_file: PathBuf, + output_file: PathBuf, + ) -> CliTypedResult> { + ExtractPeer { + host, + network_key_input_options: NetworkKeyInputOptions::from_private_key_file( + private_key_file, + ), + output_file_options: SaveFile { + output_file, + prompt_options: PromptOptions::yes(), + }, + encoding_options: Default::default(), + } + .execute() + .await + } + + pub fn init_move_dir(&mut self) { + let move_dir = TempPath::new(); + move_dir + .create_as_dir() + .expect("Expected to be able to create move temp dir"); + self.move_dir = Some(move_dir.path().to_path_buf()); + } + + #[cfg(feature = "cli-framework-test-move")] + pub fn add_move_files(&self) { + let move_dir = self.move_dir(); + let sources_dir = move_dir.join("sources"); + + let hello_blockchain_contents = include_str!( + "../../../../aptos-move/move-examples/hello_blockchain/sources/hello_blockchain.move" + ); + let source_path = sources_dir.join("hello_blockchain.move"); + write_to_file( + source_path.as_path(), + &source_path.as_display().to_string(), + hello_blockchain_contents.as_bytes(), + ) + .unwrap(); + + let hello_blockchain_test_contents = include_str!("../../../../aptos-move/move-examples/hello_blockchain/sources/hello_blockchain_test.move"); + let test_path = sources_dir.join("hello_blockchain_test.move"); + write_to_file( + test_path.as_path(), + &test_path.as_display().to_string(), + hello_blockchain_test_contents.as_bytes(), + ) + .unwrap(); + } + + pub fn move_dir(&self) -> PathBuf { + assert!(self.move_dir.is_some(), "Must have initialized the temp move directory with `CliTestFramework::init_move_dir()` first"); + self.move_dir.as_ref().cloned().unwrap() + } + + pub async fn init_package( + &self, + name: String, + account_strs: BTreeMap<&str, &str>, + framework_dir: Option, + ) -> CliTypedResult<()> { + InitPackage { + name, + package_dir: Some(self.move_dir()), + named_addresses: Self::move_manifest_named_addresses(account_strs), + prompt_options: PromptOptions { + assume_yes: false, + assume_no: true, + }, + framework_package_args: FrameworkPackageArgs { + framework_git_rev: None, + framework_local_dir: framework_dir, + skip_fetch_latest_git_deps: false, + }, + } + .execute() + .await + } + + pub async fn compile_package( + &self, + account_strs: BTreeMap<&str, &str>, + included_artifacts: Option, + ) -> CliTypedResult> { + CompilePackage { + move_options: self.move_options(account_strs), + save_metadata: false, + included_artifacts_args: IncludedArtifactsArgs { + included_artifacts: included_artifacts.unwrap_or(IncludedArtifacts::Sparse), + }, + } + .execute() + .await + } + + pub async fn test_package( + &self, + account_strs: BTreeMap<&str, &str>, + filter: Option<&str>, + ) -> CliTypedResult<&'static str> { + TestPackage { + instruction_execution_bound: 100_000, + move_options: self.move_options(account_strs), + filter: filter.map(|str| str.to_string()), + ignore_compile_warnings: false, + compute_coverage: false, + dump_state: false, + } + .execute() + .await + } + + pub async fn publish_package( + &self, + index: usize, + gas_options: Option, + account_strs: BTreeMap<&str, &str>, + included_artifacts: Option, + ) -> CliTypedResult { + PublishPackage { + move_options: self.move_options(account_strs), + txn_options: self.transaction_options(index, gas_options), + override_size_check: false, + included_artifacts_args: IncludedArtifactsArgs { + included_artifacts: included_artifacts.unwrap_or(IncludedArtifacts::Sparse), + }, + } + .execute() + .await + } + + pub async fn download_package( + &self, + index: usize, + package: String, + output_dir: PathBuf, + ) -> CliTypedResult<&'static str> { + DownloadPackage { + rest_options: self.rest_options(), + profile_options: Default::default(), + account: self.account_id(index), + package, + output_dir: Some(output_dir), + } + .execute() + .await + } + + pub async fn run_function( + &self, + index: usize, + gas_options: Option, + function_id: MemberId, + args: Vec<&str>, + type_args: Vec<&str>, + ) -> CliTypedResult { + let mut parsed_args = Vec::new(); + for arg in args { + parsed_args.push( + ArgWithType::from_str(arg) + .map_err(|err| CliError::UnexpectedError(err.to_string()))?, + ) + } + + let mut parsed_type_args = Vec::new(); + for arg in type_args { + parsed_type_args.push( + MoveType::from_str(arg) + .map_err(|err| CliError::UnexpectedError(err.to_string()))?, + ) + } + + RunFunction { + txn_options: self.transaction_options(index, gas_options), + entry_function_args: EntryFunctionArguments { + function_id, + arg_vec: ArgWithTypeVec { args: parsed_args }, + type_args: parsed_type_args, + }, + } + .execute() + .await + } + + /// Runs the given script contents using the local aptos_framework directory. + pub async fn run_script( + &self, + index: usize, + script_contents: &str, + ) -> CliTypedResult { + self.run_script_with_framework_package(index, script_contents, FrameworkPackageArgs { + framework_git_rev: None, + framework_local_dir: Some(Self::aptos_framework_dir()), + skip_fetch_latest_git_deps: false, + }) + .await + } + + /// Runs the given script contents using the aptos_framework from aptos-core git repository. + pub async fn run_script_with_default_framework( + &self, + index: usize, + script_contents: &str, + ) -> CliTypedResult { + self.run_script_with_framework_package(index, script_contents, FrameworkPackageArgs { + framework_git_rev: None, + framework_local_dir: None, + skip_fetch_latest_git_deps: false, + }) + .await + } + + /// Runs the given script with the provided framework package arguments + pub async fn run_script_with_framework_package( + &self, + index: usize, + script_contents: &str, + framework_package_args: FrameworkPackageArgs, + ) -> CliTypedResult { + // Make a temporary directory for compilation + let temp_dir = TempDir::new().map_err(|err| { + CliError::UnexpectedError(format!("Failed to create temporary directory {}", err)) + })?; + + let source_path = temp_dir.path().join("script.move"); + write_to_file( + source_path.as_path(), + &source_path.as_display().to_string(), + script_contents.as_bytes(), + ) + .unwrap(); + + RunScript { + txn_options: self.transaction_options(index, None), + compile_proposal_args: CompileScriptFunction { + script_path: Some(source_path), + compiled_script_path: None, + framework_package_args, + bytecode_version: None, + }, + arg_vec: ArgWithTypeVec { args: Vec::new() }, + type_args: Vec::new(), + } + .execute() + .await + } + + pub async fn run_script_with_script_path( + &self, + index: usize, + script_path: &str, + args: Vec, + type_args: Vec, + ) -> CliTypedResult { + RunScript { + txn_options: self.transaction_options(index, None), + compile_proposal_args: CompileScriptFunction { + script_path: Some(script_path.parse().unwrap()), + compiled_script_path: None, + framework_package_args: FrameworkPackageArgs { + framework_git_rev: None, + framework_local_dir: Some(Self::aptos_framework_dir()), + skip_fetch_latest_git_deps: false, + }, + bytecode_version: None, + }, + arg_vec: ArgWithTypeVec { args }, + type_args, + } + .execute() + .await + } + + fn aptos_framework_dir() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("..") + .join("..") + .join("aptos-move") + .join("framework") + .join("aptos-framework") + } + + pub fn move_options(&self, account_strs: BTreeMap<&str, &str>) -> MovePackageDir { + MovePackageDir { + package_dir: Some(self.move_dir()), + output_dir: None, + named_addresses: Self::named_addresses(account_strs), + skip_fetch_latest_git_deps: true, + bytecode_version: None, + } + } + + pub fn move_manifest_named_addresses( + account_strs: BTreeMap<&str, &str>, + ) -> BTreeMap { + account_strs + .iter() + .map(|(key, value)| { + ( + key.to_string(), + MoveManifestAccountWrapper::from_str(value).unwrap(), + ) + }) + .collect() + } + + pub fn named_addresses( + account_strs: BTreeMap<&str, &str>, + ) -> BTreeMap { + account_strs + .iter() + .map(|(key, value)| { + ( + key.to_string(), + AccountAddressWrapper::from_str(value).unwrap(), + ) + }) + .collect() + } + + pub fn rest_options(&self) -> RestOptions { + RestOptions::new(Some(self.endpoint.clone()), None) + } + + pub fn faucet_options(&self) -> FaucetOptions { + FaucetOptions::new(Some(self.faucet_endpoint.clone())) + } + + fn transaction_options( + &self, + index: usize, + gas_options: Option, + ) -> TransactionOptions { + TransactionOptions { + private_key_options: PrivateKeyInputOptions::from_private_key(self.private_key(index)) + .unwrap(), + sender_account: Some(self.account_id(index)), + rest_options: self.rest_options(), + gas_options: gas_options.unwrap_or_default(), + prompt_options: PromptOptions::yes(), + ..Default::default() + } + } + + fn operator_args(&self, pool_index: Option) -> OperatorArgs { + OperatorArgs { + pool_address_args: OptionalPoolAddressArgs { + pool_address: pool_index.map(|idx| self.account_id(idx)), + }, + } + } + + pub fn private_key(&self, index: usize) -> &Ed25519PrivateKey { + self.account_keys.get(index).unwrap() + } + + pub fn set_private_key( + &mut self, + index: usize, + new_key: Ed25519PrivateKey, + ) -> Ed25519PrivateKey { + // Insert the new private key into the test framework, returning the old one + mem::replace(&mut self.account_keys[index], new_key) + } + + pub fn account_id(&self, index: usize) -> AccountAddress { + *self.account_addresses.get(index).unwrap() + } + + pub async fn create_proposal( + &mut self, + index: usize, + metadata_url: &str, + script_path: PathBuf, + pool_address: AccountAddress, + is_multi_step: bool, + ) -> CliTypedResult { + SubmitProposal { + metadata_url: Url::parse(metadata_url).unwrap(), + pool_address_args: PoolAddressArgs { pool_address }, + txn_options: self.transaction_options(index, None), + is_multi_step, + compile_proposal_args: CompileScriptFunction { + script_path: Some(script_path), + compiled_script_path: None, + framework_package_args: FrameworkPackageArgs { + framework_git_rev: None, + framework_local_dir: Some(Self::aptos_framework_dir()), + skip_fetch_latest_git_deps: false, + }, + bytecode_version: None, + }, + } + .execute() + .await + } + + pub async fn vote( + &self, + index: usize, + proposal_id: u64, + yes: bool, + no: bool, + pool_addresses: Vec, + ) { + SubmitVote { + proposal_id, + yes, + no, + pool_addresses, + txn_options: self.transaction_options(index, None), + } + .execute() + .await + .expect("Successfully voted."); + } + + pub async fn verify_proposal( + &self, + proposal_id: u64, + script_path: &str, + ) -> CliTypedResult { + VerifyProposal { + proposal_id, + compile_proposal_args: CompileScriptFunction { + script_path: Some(script_path.parse().unwrap()), + compiled_script_path: None, + framework_package_args: FrameworkPackageArgs { + framework_git_rev: None, + framework_local_dir: Some(Self::aptos_framework_dir()), + skip_fetch_latest_git_deps: false, + }, + bytecode_version: None, + }, + rest_options: self.rest_options(), + profile: Default::default(), + prompt_options: PromptOptions::yes(), + } + .execute() + .await + } +} + +// ValidatorConfig/ValidatorSet doesn't match Move ValidatorSet struct, +// and json is serialized with different types from both, so hardcoding deserialization. + +fn json_account_to_balance(value: &Value) -> u64 { + u64::from_str( + value + .as_object() + .unwrap() + .get("coin") + .unwrap() + .as_object() + .unwrap() + .get("value") + .unwrap() + .as_str() + .unwrap(), + ) + .unwrap() +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct IndividualValidatorPerformance { + successful_proposals: String, + failed_proposals: String, +} + +impl IndividualValidatorPerformance { + pub fn successful_proposals(&self) -> u32 { + self.successful_proposals.parse().unwrap() + } + + pub fn failed_proposals(&self) -> u32 { + self.failed_proposals.parse().unwrap() + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ValidatorPerformance { + pub validators: Vec, +} diff --git a/movement-sdk/clis/aptos/src/test/tests.rs b/movement-sdk/clis/aptos/src/test/tests.rs new file mode 100644 index 000000000..62a8d9979 --- /dev/null +++ b/movement-sdk/clis/aptos/src/test/tests.rs @@ -0,0 +1,136 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + move_tool::{ArgWithType, FunctionArgType}, + CliResult, Tool, +}; +use clap::Parser; +use std::str::FromStr; + +/// In order to ensure that there aren't duplicate input arguments for untested CLI commands, +/// we call help on every command to ensure it at least runs +#[tokio::test] +async fn ensure_every_command_args_work() { + assert_cmd_not_panic(&["movement"]).await; + + assert_cmd_not_panic(&["movement", "account"]).await; + assert_cmd_not_panic(&["movement", "account", "create", "--help"]).await; + assert_cmd_not_panic(&["movement", "account", "create-resource-account", "--help"]).await; + assert_cmd_not_panic(&["movement", "account", "fund-with-faucet", "--help"]).await; + assert_cmd_not_panic(&["movement", "account", "list", "--help"]).await; + assert_cmd_not_panic(&["movement", "account", "lookup-address", "--help"]).await; + assert_cmd_not_panic(&["movement", "account", "rotate-key", "--help"]).await; + assert_cmd_not_panic(&["movement", "account", "transfer", "--help"]).await; + + assert_cmd_not_panic(&["movement", "config"]).await; + assert_cmd_not_panic(&["movement", "config", "generate-shell-completions", "--help"]).await; + assert_cmd_not_panic(&["movement", "config", "init", "--help"]).await; + assert_cmd_not_panic(&["movement", "config", "set-global-config", "--help"]).await; + assert_cmd_not_panic(&["movement", "config", "show-global-config"]).await; + assert_cmd_not_panic(&["movement", "config", "show-profiles"]).await; + + assert_cmd_not_panic(&["movement", "genesis"]).await; + assert_cmd_not_panic(&["movement", "genesis", "generate-genesis", "--help"]).await; + assert_cmd_not_panic(&["movement", "genesis", "generate-keys", "--help"]).await; + assert_cmd_not_panic(&["movement", "genesis", "generate-layout-template", "--help"]).await; + assert_cmd_not_panic(&["movement", "genesis", "set-validator-configuration", "--help"]).await; + assert_cmd_not_panic(&["movement", "genesis", "setup-git", "--help"]).await; + assert_cmd_not_panic(&["movement", "genesis", "generate-admin-write-set", "--help"]).await; + + assert_cmd_not_panic(&["movement", "governance"]).await; + assert_cmd_not_panic(&["movement", "governance", "execute-proposal", "--help"]).await; + assert_cmd_not_panic(&["movement", "governance", "generate-upgrade-proposal", "--help"]).await; + assert_cmd_not_panic(&["movement", "governance", "propose", "--help"]).await; + assert_cmd_not_panic(&["movement", "governance", "vote", "--help"]).await; + + assert_cmd_not_panic(&["movement", "info"]).await; + + assert_cmd_not_panic(&["movement", "init", "--help"]).await; + + assert_cmd_not_panic(&["movement", "key"]).await; + assert_cmd_not_panic(&["movement", "key", "generate", "--help"]).await; + assert_cmd_not_panic(&["movement", "key", "extract-peer", "--help"]).await; + + assert_cmd_not_panic(&["movement", "move"]).await; + assert_cmd_not_panic(&["movement", "move", "clean", "--help"]).await; + assert_cmd_not_panic(&["movement", "move", "compile", "--help"]).await; + assert_cmd_not_panic(&["movement", "move", "compile-script", "--help"]).await; + assert_cmd_not_panic(&["movement", "move", "download", "--help"]).await; + assert_cmd_not_panic(&["movement", "move", "init", "--help"]).await; + assert_cmd_not_panic(&["movement", "move", "list", "--help"]).await; + assert_cmd_not_panic(&["movement", "move", "prove", "--help"]).await; + assert_cmd_not_panic(&["movement", "move", "publish", "--help"]).await; + assert_cmd_not_panic(&["movement", "move", "run", "--help"]).await; + assert_cmd_not_panic(&["movement", "move", "run-script", "--help"]).await; + assert_cmd_not_panic(&["movement", "move", "test", "--help"]).await; + assert_cmd_not_panic(&["movement", "move", "transactional-test", "--help"]).await; + assert_cmd_not_panic(&["movement", "move", "view", "--help"]).await; + + assert_cmd_not_panic(&["movement", "node"]).await; + assert_cmd_not_panic(&["movement", "node", "check-network-connectivity", "--help"]).await; + assert_cmd_not_panic(&["movement", "node", "get-stake-pool", "--help"]).await; + assert_cmd_not_panic(&["movement", "node", "analyze-validator-performance", "--help"]).await; + assert_cmd_not_panic(&["movement", "node", "bootstrap-db-from-backup", "--help"]).await; + assert_cmd_not_panic(&["movement", "node", "initialize-validator", "--help"]).await; + assert_cmd_not_panic(&["movement", "node", "join-validator-set", "--help"]).await; + assert_cmd_not_panic(&["movement", "node", "leave-validator-set", "--help"]).await; + assert_cmd_not_panic(&["movement", "node", "run-local-testnet", "--help"]).await; + assert_cmd_not_panic(&["movement", "node", "show-validator-config", "--help"]).await; + assert_cmd_not_panic(&["movement", "node", "show-validator-set", "--help"]).await; + assert_cmd_not_panic(&["movement", "node", "show-validator-stake", "--help"]).await; + assert_cmd_not_panic(&["movement", "node", "update-consensus-key", "--help"]).await; + assert_cmd_not_panic(&[ + "movement", + "node", + "update-validator-network-addresses", + "--help", + ]) + .await; + + assert_cmd_not_panic(&["movement", "stake"]).await; + assert_cmd_not_panic(&["movement", "stake", "add-stake", "--help"]).await; + assert_cmd_not_panic(&["movement", "stake", "increase-lockup", "--help"]).await; + assert_cmd_not_panic(&["movement", "stake", "initialize-stake-owner", "--help"]).await; + assert_cmd_not_panic(&["movement", "stake", "set-delegated-voter", "--help"]).await; + assert_cmd_not_panic(&["movement", "stake", "set-operator", "--help"]).await; + assert_cmd_not_panic(&["movement", "stake", "unlock-stake", "--help"]).await; + assert_cmd_not_panic(&["movement", "stake", "withdraw-stake", "--help"]).await; +} + +/// Ensure we can parse URLs for args +#[tokio::test] +async fn ensure_can_parse_args_with_urls() { + let result = ArgWithType::from_str("string:https://aptoslabs.com").unwrap(); + matches!(result._ty, FunctionArgType::String); + assert_eq!( + result.arg, + bcs::to_bytes(&"https://aptoslabs.com".to_string()).unwrap() + ); +} + +async fn assert_cmd_not_panic(args: &[&str]) { + // When a command fails, it will have a panic in it due to an improperly setup command + // thread 'main' panicked at 'Command propose: Argument names must be unique, but 'assume-yes' is + // in use by more than one argument or group', ... + + match run_cmd(args).await { + Ok(inner) => assert!( + !inner.contains("panic"), + "Failed to not panic cmd {}: {}", + args.join(" "), + inner + ), + Err(inner) => assert!( + !inner.contains("panic"), + "Failed to not panic cmd {}: {}", + args.join(" "), + inner + ), + } +} + +async fn run_cmd(args: &[&str]) -> CliResult { + let tool: Tool = Tool::try_parse_from(args).map_err(|msg| msg.to_string())?; + tool.execute().await +} diff --git a/movement-sdk/clis/aptos/src/update/helpers.rs b/movement-sdk/clis/aptos/src/update/helpers.rs new file mode 100644 index 000000000..b54a0c759 --- /dev/null +++ b/movement-sdk/clis/aptos/src/update/helpers.rs @@ -0,0 +1,77 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::{anyhow, Context, Result}; +use self_update::{backends::github::ReleaseList, cargo_crate_version, version::bump_is_greater}; + +#[derive(Debug)] +pub struct UpdateRequiredInfo { + pub update_required: bool, + pub current_version: String, + pub latest_version: String, + pub latest_version_tag: String, +} + +/// Return information about whether an update is required. +pub fn check_if_update_required(repo_owner: &str, repo_name: &str) -> Result { + // Build a configuration for determining the latest release. + let config = ReleaseList::configure() + .repo_owner(repo_owner) + .repo_name(repo_name) + .build() + .map_err(|e| anyhow!("Failed to build configuration to fetch releases: {:#}", e))?; + + // Get the most recent releases. + let releases = config + .fetch() + .map_err(|e| anyhow!("Failed to fetch releases: {:#}", e))?; + + // Find the latest release of the CLI, in which we filter for the CLI tag. + // If the release isn't in the last 30 items (the default API page size) + // this will fail. See https://github.com/aptos-labs/aptos-core/issues/6411. + let mut releases = releases.into_iter(); + let latest_release = loop { + let release = match releases.next() { + Some(release) => release, + None => return Err(anyhow!("Failed to find latest CLI release")), + }; + if release.version.starts_with("movement-cli-") { + break release; + } + }; + let latest_version_tag = latest_release.version; + let latest_version = latest_version_tag.split("-v").last().unwrap(); + + // Return early if we're up to date already. + let current_version = cargo_crate_version!(); + let update_required = bump_is_greater(current_version, latest_version) + .context("Failed to compare current and latest CLI versions")?; + + Ok(UpdateRequiredInfo { + update_required, + current_version: current_version.to_string(), + latest_version: latest_version.to_string(), + latest_version_tag, + }) +} + +pub enum InstallationMethod { + Source, + Homebrew, + Other, +} + +impl InstallationMethod { + pub fn from_env() -> Result { + // Determine update instructions based on what we detect about the installation. + let exe_path = std::env::current_exe()?; + let installation_method = if exe_path.to_string_lossy().contains("brew") { + InstallationMethod::Homebrew + } else if exe_path.to_string_lossy().contains("target") { + InstallationMethod::Source + } else { + InstallationMethod::Other + }; + Ok(installation_method) + } +} diff --git a/movement-sdk/clis/aptos/src/update/mod.rs b/movement-sdk/clis/aptos/src/update/mod.rs new file mode 100644 index 000000000..17dab6d13 --- /dev/null +++ b/movement-sdk/clis/aptos/src/update/mod.rs @@ -0,0 +1,8 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +mod helpers; +mod tool; + +use helpers::check_if_update_required; +pub use tool::UpdateTool; diff --git a/movement-sdk/clis/aptos/src/update/tool.rs b/movement-sdk/clis/aptos/src/update/tool.rs new file mode 100644 index 000000000..1528ff85d --- /dev/null +++ b/movement-sdk/clis/aptos/src/update/tool.rs @@ -0,0 +1,145 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::{check_if_update_required, helpers::InstallationMethod}; +use crate::common::{ + types::{CliCommand, CliTypedResult}, + utils::cli_build_information, +}; +use anyhow::{anyhow, Context}; +use aptos_build_info::BUILD_OS; +use async_trait::async_trait; +use clap::Parser; +use self_update::{backends::github::Update, cargo_crate_version, Status}; +use std::process::Command; + +/// Update the CLI itself +/// +/// This can be used to update the CLI to the latest version. This is useful if you +/// installed the CLI via the install script / by downloading the binary directly. +#[derive(Debug, Parser)] +pub struct UpdateTool { + /// The owner of the repo to download the binary from. + #[clap(long, default_value = "movemnt")] + repo_owner: String, + + /// The name of the repo to download the binary from. + #[clap(long, default_value = "subnet")] // TODO: update CI/CD to include this binar release in GitHub workflows + repo_name: String, +} + +impl UpdateTool { + // Out of the box this crate assumes that you have releases named a specific way + // with the crate name, version, and target triple in a specific format. We don't + // do this with our releases, we have other GitHub releases beyond just the CLI, + // and we don't build for all major target triples, so we have to do some of the + // work ourselves first to figure out what the latest version of the CLI is and + // which binary to download based on the current OS. Then we can plug that into + // the library which takes care of the rest. + fn update(&self) -> CliTypedResult { + let installation_method = + InstallationMethod::from_env().context("Failed to determine installation method")?; + match installation_method { + InstallationMethod::Source => { + return Err( + anyhow!("Detected this CLI was built from source, refusing to update").into(), + ); + }, + InstallationMethod::Homebrew => { + return Err(anyhow!( + "Detected this CLI comes from homebrew, use `brew upgrade aptos` instead" + ) + .into()); + }, + InstallationMethod::Other => {}, + } + + let info = check_if_update_required(&self.repo_owner, &self.repo_name)?; + if !info.update_required { + return Ok(format!("CLI already up to date (v{})", info.latest_version)); + } + + // Determine the target we should download. This is necessary because we don't + // name our binary releases using the target triples nor do we build specifically + // for all major triples, so we have to generalize to one of the binaries we do + // happen to build. We figure this out based on what system the CLI was built on. + let build_info = cli_build_information(); + let target = match build_info.get(BUILD_OS).context("Failed to determine build info of current CLI")?.as_str() { + "linux-x86_64" => { + // In the case of Linux, which build to use depends on the OpenSSL + // library on the host machine. So we try to determine that here. + // This code below parses the output of the `openssl version` command, + // where the version string is the 1th (0-indexing) item in the string + // when split by whitespace. + let output = Command::new("openssl") + .args(["version"]) + .output(); + let version = match output { + Ok(output) => { + let stdout = String::from_utf8(output.stdout).unwrap(); + stdout.split_whitespace().collect::>()[1].to_string() + }, + Err(e) => { + println!("Failed to determine OpenSSL version, assuming an older version: {:#}", e); + "1.0.0".to_string() + } + }; + // On Ubuntu < 22.04 the bundled OpenSSL is version 1.x.x, whereas on + // 22.04+ it is 3.x.x. Unfortunately if you build the CLI on a system + // with one major version of OpenSSL, you cannot use it on a system + // with a different version. Accordingly, if the current system uses + // OpenSSL 3.x.x, we use the version of the CLI built on a system with + // OpenSSL 3.x.x, meaning Ubuntu 22.04. Otherwise we use the one built + // on 20.04. + if version.starts_with('3') { + "Ubuntu-22.04-x86_64" + } else { + "Ubuntu-x86_64" + } + }, + "macos-x86_64" => "MacOSX-x86_64", + "windows-x86_64" => "Windows-x86_64", + wildcard => return Err(anyhow!("Self-updating is not supported on your OS right now, please download the binary manually: {}", wildcard).into()), + }; + + // Build a new configuration that will direct the library to download the + // binary with the target version tag and target that we determined above. + let config = Update::configure() + .repo_owner(&self.repo_owner) + .repo_name(&self.repo_name) + .bin_name("movement") + .current_version(cargo_crate_version!()) + .target_version_tag(&info.latest_version_tag) + .target(target) + .build() + .map_err(|e| anyhow!("Failed to build self-update configuration: {:#}", e))?; + + // Update the binary. + let result = config + .update() + .map_err(|e| anyhow!("Failed to update Movement CLI: {:#}", e))?; + + let message = match result { + Status::UpToDate(_) => panic!("We should have caught this already"), + Status::Updated(_) => format!( + "Successfully updated from v{} to v{}", + info.current_version, info.latest_version + ), + }; + + Ok(message) + } +} + +#[async_trait] +impl CliCommand for UpdateTool { + fn command_name(&self) -> &'static str { + "Update" + } + + async fn execute(self) -> CliTypedResult { + tokio::task::spawn_blocking(move || self.update()) + .await + .context("Failed to self-update Movement CLI")? + } +} diff --git a/movement-sdk/clis/movement/Cargo.toml b/movement-sdk/clis/movement/Cargo.toml index ac7f5bd2d..c47bb4105 100644 --- a/movement-sdk/clis/movement/Cargo.toml +++ b/movement-sdk/clis/movement/Cargo.toml @@ -12,4 +12,12 @@ tokio = { workspace = true} dirs = { workspace = true } reqwest = { workspace = true } tempfile = { workspace = true } -semver = { workspace = true } \ No newline at end of file +semver = { workspace = true } +serde_json = { workspace = true } +const-str = { workspace = true } + +# sui +sui = { path = "../../../vendors/sui/crates/sui" } + +# aptos +aptos = { path = "../../../vendors/aptos-core/crates/aptos" } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/aptos/aptos.rs b/movement-sdk/clis/movement/src/aptos/aptos.rs new file mode 100644 index 000000000..f0b092800 --- /dev/null +++ b/movement-sdk/clis/movement/src/aptos/aptos.rs @@ -0,0 +1,17 @@ +use clap::Parser; +use aptos::Tool; +use clap::Subcommand; +use std::fmt::{self, Debug, Formatter}; +use crate::common::cli::Command; + +#[derive(Subcommand)] +pub enum Aptos { + #[clap(subcommand)] + Tool(Tool) +} + +impl Debug for Aptos { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "Aptos") + } +} \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/aptos/mod.rs b/movement-sdk/clis/movement/src/aptos/mod.rs index e69de29bb..7422e0292 100644 --- a/movement-sdk/clis/movement/src/aptos/mod.rs +++ b/movement-sdk/clis/movement/src/aptos/mod.rs @@ -0,0 +1,2 @@ +pub mod aptos; +pub use self::aptos::Aptos; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/cli/command.rs b/movement-sdk/clis/movement/src/common/cli/command.rs index 89bed5e04..b55972678 100644 --- a/movement-sdk/clis/movement/src/common/cli/command.rs +++ b/movement-sdk/clis/movement/src/common/cli/command.rs @@ -6,6 +6,6 @@ pub trait Command { async fn get_name(&self) -> String; - async fn execute(&self) -> Result; + async fn execute(self) -> Result; } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_dir/m1.rs b/movement-sdk/clis/movement/src/common/movement_dir/m1.rs index 78c54526f..c51e695b4 100644 --- a/movement-sdk/clis/movement/src/common/movement_dir/m1.rs +++ b/movement-sdk/clis/movement/src/common/movement_dir/m1.rs @@ -1,55 +1,50 @@ use std::path::PathBuf; use serde::{Serialize, Deserialize}; +use super::manifest::ManifestElement; +use crate::common::movement_releases::m1::M1Releases; +use crate::common::util::Version; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct M1Manifest { - pub m1_source : Option, - pub m1_subnet_binary : Option, - pub m1_proxy_binary : Option + pub m1_source_with_submodules : ManifestElement, + pub m1_subnet_binary : ManifestElement, } impl M1Manifest { - pub fn new(m1_source : Option, m1_subnet_binary : Option, m1_proxy_binary : Option) -> Self { + pub fn new(m1_source_with_submodules : ManifestElement, m1_subnet_binary : ManifestElement, m1_proxy_binary : ManifestElement) -> Self { Self { - m1_source, + m1_source_with_submodules, m1_subnet_binary, - m1_proxy_binary } } - pub fn register_m1_source_path(&mut self, m1_source : PathBuf) { - self.m1_source = Some(m1_source); - } - - pub fn remove_m1_source_path(&mut self) { - self.m1_source = None; - } - - pub fn register_m1_subnet_binary_path(&mut self, subnet_binary : PathBuf) { - self.m1_subnet_binary = Some(subnet_binary); - } - - pub fn remove_m1_subnet_binary_path(&mut self) { - self.m1_subnet_binary = None; - } - - pub fn register_m1_proxy_binary_path(&mut self, proxy_binary : PathBuf) { - self.m1_proxy_binary = Some(proxy_binary); - } - - pub fn remove_m1_proxy_path(&mut self) { - self.m1_proxy_binary = None; + pub async fn get_all_defined(&self) -> Result<(), anyhow::Error> { + tokio::try_join!( + self.m1_source_with_submodules.write_if_path_defined(), + self.m1_subnet_binary.write_if_path_defined() + )?; + Ok(()) } } impl Default for M1Manifest { fn default() -> Self { + + let m1_releases = M1Releases::from_os_arch(&Version::Latest); + Self { - m1_source: None, - m1_subnet_binary: None, - m1_proxy_binary: None + + m1_source_with_submodules : ManifestElement::new( + m1_releases.m1_source_with_submodules().clone(), + None + ), + m1_subnet_binary : ManifestElement::new( + m1_releases.m1_subnet_binary().clone(), + None + ) + } } } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_dir/manifest.rs b/movement-sdk/clis/movement/src/common/movement_dir/manifest.rs index c301386b3..4b4578c89 100644 --- a/movement-sdk/clis/movement/src/common/movement_dir/manifest.rs +++ b/movement-sdk/clis/movement/src/common/movement_dir/manifest.rs @@ -1,17 +1,106 @@ use std::path::PathBuf; use serde::{Serialize, Deserialize}; +use serde_json; use super::m1::M1Manifest; +use super::super::movement_releases::Release; +use std::fs; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ManifestElement { + pub release : Release, + pub path : Option +} + +impl ManifestElement { + + pub fn new(release : Release, path : Option) -> Self { + Self { + release, + path + } + } + + pub fn register_path(&mut self, path : PathBuf) { + self.path = Some(path); + } + + pub fn remove_path(&mut self) { + self.path = None; + } + + pub fn release(&self) -> &Release { + &self.release + } + + pub fn path(&self) -> Option<&PathBuf> { + self.path.as_ref() + } + + pub async fn write(&self) -> Result<(), anyhow::Error> { + match self.path { + Some(ref path) => { + self.release.to_file(path).await + }, + None => { + Err(anyhow::anyhow!("No path registered for this manifest element")) + } + } + } + + pub async fn write_if_path_defined(&self) -> Result<(), anyhow::Error> { + match self.path { + Some(ref path) => { + self.release.to_file(path).await + }, + None => { + Ok(()) + } + } + } + + // todo: update to include directories + pub async fn remove(&self) -> Result<(), anyhow::Error> { + match self.path { + Some(ref path) => { + fs::remove_file(path)?; + Ok(()) + }, + None => { + Err(anyhow::anyhow!("No path registered for this manifest element")) + } + } + } + + // todo: update to include directories + pub async fn remove_if_path_defined(&self) -> Result<(), anyhow::Error> { + match self.path { + Some(ref path) => { + fs::remove_file(path)?; + Ok(()) + }, + None => { + Ok(()) + } + } + } + + pub async fn register_and_write(&mut self, path : PathBuf) -> Result<(), anyhow::Error> { + self.register_path(path); + self.write().await + } + +} #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MovementDirManifest { - pub movement_dir : Option, - pub movement_binary : Option, - pub m1 : Option + pub movement_dir : ManifestElement, + pub movement_binary : ManifestElement, + pub m1 : M1Manifest } impl MovementDirManifest { - pub fn new(movement_dir : Option, movement_binary : Option, m1 : Option) -> Self { + pub fn new(movement_dir : ManifestElement, movement_binary : ManifestElement, m1 : M1Manifest) -> Self { Self { movement_dir, movement_binary, @@ -19,20 +108,35 @@ impl MovementDirManifest { } } - pub fn register_movement_dir_path(&mut self, movement_dir : PathBuf) { - self.movement_dir = Some(movement_dir); - } + pub fn update_manifest_file(&self) -> Result<(), anyhow::Error> { - pub fn remove_movement_dir_path(&mut self) { - self.movement_dir = None; - } + match self.movement_dir.path() { + Some(path) => { + // Serialize `self` to a JSON string + let serialized = serde_json::to_string(&self)?; + + // Construct the file path + let file_path = path.join("manifest.json"); - pub fn register_movement_binary_path(&mut self, movement_binary : PathBuf) { - self.movement_binary = Some(movement_binary); + // Write the serialized data to the file + fs::write(file_path, serialized)?; + + Ok(()) + }, + None => { + Err(anyhow::anyhow!("No path registered for this manifest element")) + } + } + } - pub fn remove_movement_binary_path(&mut self) { - self.movement_binary = None; + pub async fn get_all_defined(&self) -> Result<(), anyhow::Error> { + tokio::try_join!( + self.movement_dir.write_if_path_defined(), + self.movement_binary.write_if_path_defined(), + self.m1.get_all_defined() + )?; + Ok(()) } } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_dir/mod.rs b/movement-sdk/clis/movement/src/common/movement_dir/mod.rs index 231f8ed50..bde7a2a38 100644 --- a/movement-sdk/clis/movement/src/common/movement_dir/mod.rs +++ b/movement-sdk/clis/movement/src/common/movement_dir/mod.rs @@ -1,3 +1,4 @@ pub mod movement_dir; +pub use movement_dir::DefaultInMovementDir; pub mod m1; pub mod manifest; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs b/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs index dbfa49b9f..c9e1155f3 100644 --- a/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs +++ b/movement-sdk/clis/movement/src/common/movement_dir/movement_dir.rs @@ -2,6 +2,10 @@ use std::path::PathBuf; use serde::{Serialize, Deserialize}; use super::manifest::MovementDirManifest; +pub trait DefaultInMovementDir { + fn default_in_movement_dir(path : &PathBuf) -> Self; +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MovementDir { pub manifest : MovementDirManifest @@ -16,5 +20,18 @@ impl MovementDir { } } + pub fn manifest(&self) -> &MovementDirManifest { + &self.manifest + } + + pub fn update_manifest_file(&self) -> Result<(), anyhow::Error> { + self.manifest.update_manifest_file()?; + Ok(()) + } + + pub async fn get_all_defined(&self) -> Result<(), anyhow::Error> { + self.manifest.get_all_defined().await?; + Ok(()) + } } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_releases/m1.rs b/movement-sdk/clis/movement/src/common/movement_releases/m1.rs index 763966f36..691997f1c 100644 --- a/movement-sdk/clis/movement/src/common/movement_releases/m1.rs +++ b/movement-sdk/clis/movement/src/common/movement_releases/m1.rs @@ -2,11 +2,8 @@ use serde::{Serialize, Deserialize}; use super::{Release, movement_releases::MovementGitHubRelease}; use crate::common::util::Version; -pub static M1_GITHUB_RELEASES : &str = "https://github.com/movemntdev/M1/releases"; - #[derive(Debug, Clone, Serialize, Deserialize)] pub struct M1GitHubReleases { - pub m1_source : Release, pub m1_source_with_submodules : Release, pub m1_subnet_binary : Release, } @@ -15,23 +12,19 @@ impl M1GitHubReleases { pub fn from_os_arch(version : &Version) -> Self { Self { - m1_source : MovementGitHubRelease::new( - "movemntdev".to_string(), - "M1".to_string(), - version.clone(), - "m1-source".to_string() - ).into(), m1_source_with_submodules : MovementGitHubRelease::new( "movemntdev".to_string(), "M1".to_string(), version.clone(), - "m1-source-with-submodules".to_string() + "m1-source-with-submodules".to_string(), + ".tar.gz".to_string() ).into(), m1_subnet_binary : MovementGitHubRelease::new( "movemntdev".to_string(), "M1".to_string(), version.clone(), - "subnet".to_string() + "subnet".to_string(), + "".to_string() ).into() } @@ -50,12 +43,6 @@ impl M1Releases { Self::GitHub(M1GitHubReleases::from_os_arch(version)) } - pub fn m1_source(&self) -> &Release { - match self { - Self::GitHub(releases) => &releases.m1_source - } - } - pub fn m1_source_with_submodules(&self) -> &Release { match self { Self::GitHub(releases) => &releases.m1_source_with_submodules @@ -73,11 +60,6 @@ impl M1Releases { #[cfg(test)] mod test { - use std::{ - thread::sleep, - time::Duration as duration - }; - use super::*; // this is primarily for a manual check right now @@ -94,13 +76,11 @@ mod test { // get all of the releases m1_releases.m1_subnet_binary().to_file(&tmp_dir.path().join("subnet")).await?; - m1_releases.m1_source().to_file(&tmp_dir.path().join("m1-source")).await?; - m1_releases.m1_source_with_submodules().to_file(&tmp_dir.path().join("m1-source-with-submodules")).await?; + m1_releases.m1_source_with_submodules().to_file(&tmp_dir.path().join("m1-source-with-submodules.tar.gz")).await?; // check that they are there assert!(tmp_dir.path().join("subnet").exists()); - assert!(tmp_dir.path().join("m1-source").exists()); - assert!(tmp_dir.path().join("m1-source-with-submodules").exists()); + assert!(tmp_dir.path().join("m1-source-with-submodules.tar.gz").exists()); Ok(()) diff --git a/movement-sdk/clis/movement/src/common/movement_releases/movement_cli.rs b/movement-sdk/clis/movement/src/common/movement_releases/movement_cli.rs index 4a8d91857..b77a21ee6 100644 --- a/movement-sdk/clis/movement/src/common/movement_releases/movement_cli.rs +++ b/movement-sdk/clis/movement/src/common/movement_releases/movement_cli.rs @@ -1,12 +1,72 @@ use serde::{Serialize, Deserialize}; -use super::Release; +use super::{Release, movement_releases::MovementGitHubRelease}; +use crate::common::util::Version; #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MovementGitHubReleases { - movement_binary : Release, +pub struct MovementCliGithubReleases { + pub movement_cli_binary : Release, +} + +impl MovementCliGithubReleases { + + pub fn from_os_arch(version : &Version) -> Self { + Self { + movement_cli_binary : MovementGitHubRelease::new( + "movemntdev".to_string(), + "M1".to_string(), + version.clone(), + "movement".to_string(), + "".to_string() + ).into() + } + } + } #[derive(Debug, Clone, Serialize, Deserialize)] -pub enum MovementCliReleases { - GitHub(MovementGitHubReleases) +pub enum M1Releases { + GitHub(MovementCliGithubReleases) +} + +impl M1Releases { + + pub fn from_os_arch(version : &Version) -> Self { + Self::GitHub(MovementCliGithubReleases::from_os_arch(version)) + } + + pub fn movement_cli_binary(&self) -> &Release { + match self { + Self::GitHub(releases) => &releases.movement_cli_binary + } + } + +} + +#[cfg(test)] +mod test { + + use super::*; + + // this is primarily for a manual check right now + // run this and check the dir which is printed. + #[tokio::test] + async fn test_m1_github_releases() -> Result<(), anyhow::Error> { + + let m1_releases = M1Releases::from_os_arch(&Version::Latest); + println!("{:?}", m1_releases); + + // tmp dir + let tmp_dir = tempfile::tempdir().unwrap(); + println!("tmp_dir: {:?}", tmp_dir); + + // get all of the releases + m1_releases.movement_cli_binary().to_file(&tmp_dir.path().join("movement")).await?; + + // check that they are there + assert!(tmp_dir.path().join("movement").exists()); + + Ok(()) + + } + } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/common/movement_releases/movement_releases.rs b/movement-sdk/clis/movement/src/common/movement_releases/movement_releases.rs index e23f6078b..bf6eba3aa 100644 --- a/movement-sdk/clis/movement/src/common/movement_releases/movement_releases.rs +++ b/movement-sdk/clis/movement/src/common/movement_releases/movement_releases.rs @@ -18,27 +18,29 @@ pub struct MovementGitHubRelease { pub owner : String, pub repo : String, pub version : Version, - pub asset : String + pub asset : String, + pub suffix : String } impl MovementGitHubRelease { - pub fn new(owner : String, repo : String, version : Version, asset : String) -> Self { + pub fn new(owner : String, repo : String, version : Version, asset : String, suffix : String) -> Self { Self { owner, repo, version, - asset + asset, + suffix } } pub fn os_arch_release_url(&self) -> String { match &self.version { Version::Latest => { - format!("https://github.com/{}/{}/releases/latest/download/{}-{}-{}", self.owner, self.repo, self.asset, std::env::consts::OS, std::env::consts::ARCH) + format!("https://github.com/{}/{}/releases/latest/download/{}-{}-{}{}", self.owner, self.repo, self.asset, std::env::consts::OS, std::env::consts::ARCH, self.suffix) }, Version::Version(version) => { - format!("https://github.com/{}/{}/releases/download/{}/{}-{}-{}", self.owner, self.repo, version, self.asset, std::env::consts::OS, std::env::consts::ARCH) + format!("https://github.com/{}/{}/releases/download/{}/{}-{}-{}{}", self.owner, self.repo, version, self.asset, std::env::consts::OS, std::env::consts::ARCH, self.suffix) } } } diff --git a/movement-sdk/clis/movement/src/ctl/ctl.rs b/movement-sdk/clis/movement/src/ctl/ctl.rs index 002248ff6..6cdfc3195 100644 --- a/movement-sdk/clis/movement/src/ctl/ctl.rs +++ b/movement-sdk/clis/movement/src/ctl/ctl.rs @@ -7,6 +7,10 @@ use super::{ }; #[derive(Subcommand, Debug)] +#[clap( + rename_all = "kebab-case", + about = "Control Movement services" +)] pub enum Ctl { #[clap(subcommand)] Start(Start), diff --git a/movement-sdk/clis/movement/src/ctl/start/start.rs b/movement-sdk/clis/movement/src/ctl/start/start.rs index 6a043ba63..1b02d17df 100644 --- a/movement-sdk/clis/movement/src/ctl/start/start.rs +++ b/movement-sdk/clis/movement/src/ctl/start/start.rs @@ -2,6 +2,10 @@ use async_trait::async_trait; use clap::Subcommand; #[derive(Subcommand, Debug)] +#[clap( + rename_all = "kebab-case", + about = "Start a Movement service" +)] pub enum Start { } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/status/status.rs b/movement-sdk/clis/movement/src/ctl/status/status.rs index 7238b9cb4..a78aff520 100644 --- a/movement-sdk/clis/movement/src/ctl/status/status.rs +++ b/movement-sdk/clis/movement/src/ctl/status/status.rs @@ -2,6 +2,10 @@ use async_trait::async_trait; use clap::Subcommand; #[derive(Subcommand, Debug)] +#[clap( + rename_all = "kebab-case", + about = "Get the status of a Movement service" +)] pub enum Status { } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/ctl/stop/stop.rs b/movement-sdk/clis/movement/src/ctl/stop/stop.rs index 46d20eb19..e92b1ce2f 100644 --- a/movement-sdk/clis/movement/src/ctl/stop/stop.rs +++ b/movement-sdk/clis/movement/src/ctl/stop/stop.rs @@ -2,6 +2,10 @@ use async_trait::async_trait; use clap::Subcommand; #[derive(Subcommand, Debug)] +#[clap( + rename_all = "kebab-case", + about = "Stop a Movement service" +)] pub enum Stop { } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/lib.rs b/movement-sdk/clis/movement/src/lib.rs index 123ea19a0..9fa1c8289 100644 --- a/movement-sdk/clis/movement/src/lib.rs +++ b/movement-sdk/clis/movement/src/lib.rs @@ -2,17 +2,84 @@ pub mod common; pub mod manage; pub mod ctl; -pub mod aptos; -pub mod sui; +// pub mod aptos; +// pub mod sui; pub mod canonical; +use clap::*; + use async_trait::async_trait; -use clap::Parser; +use manage::Manage; use ctl::Ctl; +use sui::sui_commands::SuiCommand; +use aptos::Tool; +use crate::common::cli::Command; +// use crate::aptos::Aptos; +// use crate::sui::Sui; + +const VERSION: &str = const_str::concat!(env!("CARGO_PKG_VERSION")); + -#[derive(Parser, Debug)] -#[clap(name = "movement", author, version, propagate_version = true)] -pub enum Movement { +#[derive(Parser)] +#[clap(rename_all = "kebab-case")] +pub enum MovementCommand { + #[clap(subcommand)] + Manage(Manage), #[clap(subcommand)] Ctl(Ctl), + #[clap(subcommand, about = "Run Aptos commands")] + Aptos(Tool), + #[clap(subcommand, about = "Run Sui commands")] + Sui(SuiCommand) +} + +#[async_trait::async_trait] +impl Command for MovementCommand { + + async fn get_name(&self) -> String { + "movement".to_string() + } + + async fn execute(self) -> Result { + + match self { + MovementCommand::Manage(manage) => { + Ok("SUCCESS".to_string()) + }, + MovementCommand::Ctl(ctl) => { + // ctl.execute().await?; + Ok("SUCCESS".to_string()) + }, + MovementCommand::Aptos(aptos) => { + aptos.execute().await.map_err( + |e| anyhow::anyhow!("aptos error: {:?}", e) + )?; + Ok("SUCCESS".to_string()) + }, + MovementCommand::Sui(sui) => { + sui.execute().await?; + Ok("SUCCESS".to_string()) + }, + _ => { + Ok("NOT FOUND".to_string()) + } + } + + } + +} + +#[derive(Parser)] +#[clap( + // name = env!("CARGO_BIN_NAME"), + name = "movement", + about = "A network of Move-based blockchains", + rename_all = "kebab-case", + author, + version = VERSION, + propagate_version = true, +)] +pub struct Movement { + #[clap(subcommand)] + pub command: MovementCommand } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/main.rs b/movement-sdk/clis/movement/src/main.rs index 4c5f216c5..4790e089f 100644 --- a/movement-sdk/clis/movement/src/main.rs +++ b/movement-sdk/clis/movement/src/main.rs @@ -1,15 +1,19 @@ #![forbid(unsafe_code)] -#[cfg(unix)] -#[global_allocator] -static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; - -use clap::Parser; -use std::process::exit; +use clap::*; +use movement::Movement; +use movement::common::cli::Command; #[tokio::main] -async fn main() { +async fn main() -> Result<(), anyhow::Error> { // todo: parse clap + let movement = Movement::parse(); + + let res = movement.command.execute().await; + + println!("res: {:?}", res); + + Ok(()) } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/manage/install/install.rs b/movement-sdk/clis/movement/src/manage/install/install.rs index c351c73e0..fb0839ab3 100644 --- a/movement-sdk/clis/movement/src/manage/install/install.rs +++ b/movement-sdk/clis/movement/src/manage/install/install.rs @@ -5,6 +5,10 @@ use clap::{Subcommand, Parser}; pub struct All; #[derive(Subcommand, Debug)] +#[clap( + rename_all = "kebab-case", + about = "Install Movement artifacts" +)] pub enum Install { All(All) } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/manage/manage.rs b/movement-sdk/clis/movement/src/manage/manage.rs index 446a45990..5f7e8228d 100644 --- a/movement-sdk/clis/movement/src/manage/manage.rs +++ b/movement-sdk/clis/movement/src/manage/manage.rs @@ -1,7 +1,13 @@ use async_trait::async_trait; use clap::Subcommand; +use super::install::Install; #[derive(Subcommand, Debug)] +#[clap( + rename_all = "kebab-case", + about = "Manage the Movement CLI and artifacts" +)] pub enum Manage { - + #[clap(subcommand)] + Install(Install) } \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/sui/mod.rs b/movement-sdk/clis/movement/src/sui/mod.rs index e69de29bb..b165ffa09 100644 --- a/movement-sdk/clis/movement/src/sui/mod.rs +++ b/movement-sdk/clis/movement/src/sui/mod.rs @@ -0,0 +1,2 @@ +pub mod sui; +pub use self::sui::Sui; \ No newline at end of file diff --git a/movement-sdk/clis/movement/src/sui/sui.rs b/movement-sdk/clis/movement/src/sui/sui.rs new file mode 100644 index 000000000..a4cdccaac --- /dev/null +++ b/movement-sdk/clis/movement/src/sui/sui.rs @@ -0,0 +1,54 @@ +pub use clap::Parser; +use sui::sui_commands::SuiCommand; +use clap::{FromArgMatches, ArgMatches, Subcommand}; +use std::fmt::{self, Debug, Formatter}; +use crate::common::cli::Command; + + + +#[derive(Subcommand)] +pub struct Sui { + #[clap(subcommand)] + command: SuiCommand +} + +impl Debug for Sui { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "Sui") + } +} + +#[async_trait::async_trait] +impl Command for Sui { + + async fn get_name(&self) -> String { + "sui".to_string() + } + + async fn execute(self) -> Result { + #[cfg(windows)] + colored::control::set_virtual_terminal(true).unwrap(); + + let args = Args::parse(); + let _guard = match args.command { + SuiCommand::Console { .. } + | SuiCommand::Client { .. } + | SuiCommand::KeyTool { .. } + | SuiCommand::Move { .. } => telemetry_subscribers::TelemetryConfig::new() + .with_log_level("error") + .with_env() + .init(), + _ => telemetry_subscribers::TelemetryConfig::new() + .with_env() + .init(), + }; + + debug!("Sui CLI version: {VERSION}"); + + exit_main!(args.command.execute().await); + + Ok("SUCCESS".to_string()) + + } + +} \ No newline at end of file diff --git a/movement-sdk/rust-toolchain b/movement-sdk/rust-toolchain new file mode 100644 index 000000000..832e9afb6 --- /dev/null +++ b/movement-sdk/rust-toolchain @@ -0,0 +1 @@ +1.70.0 diff --git a/vendors/aptos-core b/vendors/aptos-core index e4c39ec37..6880a20a8 160000 --- a/vendors/aptos-core +++ b/vendors/aptos-core @@ -1 +1 @@ -Subproject commit e4c39ec37274a141b2b4c859285897f5b43d8a71 +Subproject commit 6880a20a8617a1145955bbda965e5dffa14f268d From f5f8292b09c27e6999ce1d7699e9bb64d44992a0 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 1 Dec 2023 21:36:08 -0800 Subject: [PATCH 38/58] feat: updating submodules. --- vendors/aptos-core | 2 +- vendors/sui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vendors/aptos-core b/vendors/aptos-core index 6880a20a8..d3642c1c7 160000 --- a/vendors/aptos-core +++ b/vendors/aptos-core @@ -1 +1 @@ -Subproject commit 6880a20a8617a1145955bbda965e5dffa14f268d +Subproject commit d3642c1c708269e500c08c9ef9319c248eb6ae6c diff --git a/vendors/sui b/vendors/sui index 845182094..76c79d96c 160000 --- a/vendors/sui +++ b/vendors/sui @@ -1 +1 @@ -Subproject commit 845182094b64f517dcbeab22f6da903f4f5bc7ec +Subproject commit 76c79d96cb15cbadd579832913e5f8922de46da8 From f9f536dd23ef0f4462b281068dc4011a0e563526 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 1 Dec 2023 21:37:23 -0800 Subject: [PATCH 39/58] fix: lock canonical. --- canonical/Cargo.lock | 46 ++++++++++++++++++++++---------------------- vendors/aptos-core | 2 +- vendors/sui | 2 +- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/canonical/Cargo.lock b/canonical/Cargo.lock index ecdd33339..a281a151d 100644 --- a/canonical/Cargo.lock +++ b/canonical/Cargo.lock @@ -315,7 +315,7 @@ dependencies = [ "aptos-metrics-core", "aptos-types", "bcs 0.1.4", - "clap 4.4.7", + "clap 4.4.10", "dashmap", "itertools 0.10.5", "move-core-types 0.0.4-canonical-aptos", @@ -391,7 +391,7 @@ dependencies = [ "better_any", "blake2-rfc", "blst", - "clap 4.4.7", + "clap 4.4.10", "codespan-reporting", "curve25519-dalek", "flate2", @@ -447,7 +447,7 @@ dependencies = [ "aptos-types", "aptos-vm-types", "bcs 0.1.4", - "clap 4.4.7", + "clap 4.4.10", "move-binary-format 0.0.3-canonical-aptos", "move-core-types 0.0.4-canonical-aptos", "move-model 0.1.0-canonical-aptos", @@ -602,7 +602,7 @@ dependencies = [ "anyhow", "aptos-types", "bcs 0.1.4", - "clap 4.4.7", + "clap 4.4.10", "heck 0.3.3", "move-core-types 0.0.4-canonical-aptos", "once_cell", @@ -2068,9 +2068,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.7" +version = "4.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" dependencies = [ "clap_builder", "clap_derive", @@ -2078,9 +2078,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.7" +version = "4.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" dependencies = [ "anstream", "anstyle", @@ -5201,7 +5201,7 @@ version = "0.0.1-canonical-aptos" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.7", + "clap 4.4.10", "codespan-reporting", "difference", "hex", @@ -5229,7 +5229,7 @@ version = "0.0.1-canonical-sui" dependencies = [ "anyhow", "bcs 0.1.6", - "clap 4.4.7", + "clap 4.4.10", "codespan-reporting", "hex", "move-binary-format 0.0.3-canonical-sui", @@ -5292,7 +5292,7 @@ version = "0.1.0-canonical-aptos" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.7", + "clap 4.4.10", "codespan", "colored", "move-binary-format 0.0.3-canonical-aptos", @@ -5311,7 +5311,7 @@ version = "0.1.0-canonical-sui" dependencies = [ "anyhow", "bcs 0.1.6", - "clap 4.4.7", + "clap 4.4.10", "codespan", "colored", "move-binary-format 0.0.3-canonical-sui", @@ -5328,7 +5328,7 @@ name = "move-disassembler" version = "0.1.0-canonical-aptos" dependencies = [ "anyhow", - "clap 4.4.7", + "clap 4.4.10", "colored", "move-binary-format 0.0.3-canonical-aptos", "move-bytecode-source-map 0.1.0-canonical-aptos", @@ -5346,7 +5346,7 @@ version = "0.1.0-canonical-sui" dependencies = [ "anyhow", "bcs 0.1.6", - "clap 4.4.7", + "clap 4.4.10", "colored", "hex", "move-binary-format 0.0.3-canonical-sui", @@ -5557,7 +5557,7 @@ version = "0.1.0-canonical-aptos" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.7", + "clap 4.4.10", "colored", "dirs-next", "itertools 0.10.5", @@ -5591,7 +5591,7 @@ name = "move-package" version = "0.1.0-canonical-sui" dependencies = [ "anyhow", - "clap 4.4.7", + "clap 4.4.10", "colored", "move-abigen 0.1.0-canonical-sui", "move-binary-format 0.0.3-canonical-sui", @@ -5633,7 +5633,7 @@ dependencies = [ "anyhow", "async-trait", "atty", - "clap 4.4.7", + "clap 4.4.10", "codespan", "codespan-reporting", "futures 0.3.28", @@ -5667,7 +5667,7 @@ name = "move-prover" version = "0.1.0-canonical-sui" dependencies = [ "anyhow", - "clap 4.4.7", + "clap 4.4.10", "codespan-reporting", "itertools 0.10.5", "log", @@ -6375,7 +6375,7 @@ dependencies = [ "axum", "bytes", "cfg-if", - "clap 4.4.7", + "clap 4.4.10", "eyre", "fastcrypto", "futures 0.3.28", @@ -10127,7 +10127,7 @@ dependencies = [ name = "sui-protocol-config" version = "0.1.0-canonical-sui" dependencies = [ - "clap 4.4.7", + "clap 4.4.10", "insta", "schemars", "serde 1.0.190", @@ -10154,7 +10154,7 @@ dependencies = [ "anyhow", "async-trait", "bcs 0.1.6", - "clap 4.4.7", + "clap 4.4.10", "colored", "fastcrypto", "futures 0.3.28", @@ -10213,7 +10213,7 @@ dependencies = [ "byteorder", "bytes", "chrono", - "clap 4.4.7", + "clap 4.4.10", "eyre", "fastcrypto", "futures 0.3.28", @@ -10531,7 +10531,7 @@ dependencies = [ "atomic_float", "bytes", "bytes-varint", - "clap 4.4.7", + "clap 4.4.10", "crossterm", "futures 0.3.28", "once_cell", diff --git a/vendors/aptos-core b/vendors/aptos-core index e4c39ec37..d3642c1c7 160000 --- a/vendors/aptos-core +++ b/vendors/aptos-core @@ -1 +1 @@ -Subproject commit e4c39ec37274a141b2b4c859285897f5b43d8a71 +Subproject commit d3642c1c708269e500c08c9ef9319c248eb6ae6c diff --git a/vendors/sui b/vendors/sui index 845182094..76c79d96c 160000 --- a/vendors/sui +++ b/vendors/sui @@ -1 +1 @@ -Subproject commit 845182094b64f517dcbeab22f6da903f4f5bc7ec +Subproject commit 76c79d96cb15cbadd579832913e5f8922de46da8 From dd5ff32f81911dc41bf9c4f6d70630894453d50c Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 1 Dec 2023 21:46:17 -0800 Subject: [PATCH 40/58] feat: updating build. --- .github/workflows/release.yml | 48 +++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cf1d593b6..d27d541a1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -93,8 +93,12 @@ jobs: - name: Build binaries run: | cd "$GITHUB_WORKSPACE/m1" - cargo clean - RUSTFLAGS="--cfg tokio_unstable" cargo build --release + cargo build --release + + - name: Build Mac binaries + run: | + cd "$GITHUB_WORKSPACE/movement-sdk" + cargo build --release - name: Upload subnet uses: actions/upload-release-asset@v1 @@ -112,7 +116,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ needs.prepare-release.outputs.release_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this reference for more info: https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsid - asset_path: ./m1/target/release/movement + asset_path: ./movement-sdk/target/release/movement asset_name: movement-x86_64-linux asset_content_type: application/octet-stream @@ -146,6 +150,11 @@ jobs: cd "$GITHUB_WORKSPACE/m1" cargo build --release --target x86_64-apple-darwin + - name: Build Mac binaries + run: | + cd "$GITHUB_WORKSPACE/movement-sdk" + cargo build --release --target x86_64-apple-darwin + - name: Upload subnet uses: actions/upload-release-asset@v1 env: @@ -162,7 +171,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ needs.prepare-release.outputs.release_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this reference for more info: https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsid - asset_path: ./m1/target/release/movement + asset_path: ./movement-sdk/target/release/movement asset_name: movement-x86_64-mac asset_content_type: application/octet-stream @@ -194,6 +203,11 @@ jobs: run: | cd "$GITHUB_WORKSPACE/m1" cargo build --release + + - name: Build Mac binaries + run: | + cd "$GITHUB_WORKSPACE/movement-sdk" + cargo build --release - name: Upload subnet uses: actions/upload-release-asset@v1 @@ -211,7 +225,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ needs.prepare-release.outputs.release_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this reference for more info: https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsid - asset_path: ./m1/target/release/movement + asset_path: ./movement-sdk/target/release/movement asset_name: movement-aarch64-linux asset_content_type: application/octet-stream @@ -238,6 +252,11 @@ jobs: run: | cd "$GITHUB_WORKSPACE/m1" cargo build --release --target aarch64-apple-darwin + + - name: Build Mac binaries + run: | + cd "$GITHUB_WORKSPACE/movement-sdk" + cargo build --release --target aarch64-apple-darwin - name: Upload subnet uses: actions/upload-release-asset@v1 @@ -255,7 +274,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ needs.prepare-release.outputs.release_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this reference for more info: https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsid - asset_path: ./m1/target/release/movement + asset_path: ./movement-sdk/target/release/movement asset_name: movement-aarch64-mac asset_content_type: application/octet-stream @@ -287,6 +306,11 @@ jobs: cd "$GITHUB_WORKSPACE/m1" cargo build --release --target x86_64-pc-windows-gnu + - name: Build Mac binaries + run: | + cd "$GITHUB_WORKSPACE/movement-sdk" + cargo build --release --target x86_64-pc-windows-gnu + - name: Upload subnet uses: actions/upload-release-asset@v1 env: @@ -303,7 +327,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ needs.prepare-release.outputs.release_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this reference for more info: https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsid - asset_path: ./m1/target/release/movement + asset_path: ./movement-sdk/target/release/movement asset_name: movement-x86_64-windows asset_content_type: application/octet-stream @@ -331,7 +355,8 @@ jobs: IMAGE_TAG: ${{ needs.prepare-release.outputs.release_tag }} # Or any other tag you'd like run: | docker buildx create --use - docker buildx build --build-arg VERSION=${{ needs.prepare-release.outputs.release_tag }} -f ./docker/cli.dockerfile --platform linux/amd64,linux/arm64 -t $DOCKER_HUB_REPOSITORY:$IMAGE_TAG -t $DOCKER_HUB_REPOSITORY:latest . --push + docker buildx build --build-arg VERSION=${{ needs.prepare-release.outputs.release_tag }} -f ./docker/cli.dockerfile --platform linux/amd64,linux/arm64 -t $DOCKER_HUB_REPOSITORY:$IMAGE_TAG $(if [ "${{ github.ref }}" = "refs/heads/main" ]; then echo "-t $DOCKER_HUB_REPOSITORY:latest"; fi) . --push + - name: Full developer container env: @@ -339,7 +364,8 @@ jobs: IMAGE_TAG: ${{ needs.prepare-release.outputs.release_tag }} run: | docker buildx create --use - docker buildx build --build-arg VERSION=${{ needs.prepare-release.outputs.release_tag }} -f ./docker/dev.dockerfile --platform linux/amd64,linux/arm64 -t $DOCKER_HUB_REPOSITORY:$IMAGE_TAG -t $DOCKER_HUB_REPOSITORY:latest . --push + docker buildx build --build-arg VERSION=${{ needs.prepare-release.outputs.release_tag }} -f ./docker/dev.dockerfile --platform linux/amd64,linux/arm64 -t $DOCKER_HUB_REPOSITORY:$IMAGE_TAG $(if [ "${{ github.ref }}" = "refs/heads/main" ]; then echo "-t $DOCKER_HUB_REPOSITORY:latest"; fi) . --push + - name: General container # for now this is just the dev container env: @@ -347,8 +373,8 @@ jobs: IMAGE_TAG: ${{ needs.prepare-release.outputs.release_tag }} run: | docker buildx create --use - docker buildx build --build-arg VERSION=${{ needs.prepare-release.outputs.release_tag }} -f ./docker/dev.dockerfile --platform linux/amd64,linux/arm64 -t $DOCKER_HUB_REPOSITORY:$IMAGE_TAG -t $DOCKER_HUB_REPOSITORY:latest . --push - + docker buildx build --build-arg VERSION=${{ needs.prepare-release.outputs.release_tag }} -f ./docker/dev.dockerfile --platform linux/amd64,linux/arm64 -t $DOCKER_HUB_REPOSITORY:$IMAGE_TAG $(if [ "${{ github.ref }}" = "refs/heads/main" ]; then echo "-t $DOCKER_HUB_REPOSITORY:latest"; fi) . --push + run-tests: needs: - prepare-release From 41380a84743c130bd9751125682a7625e1ffee72 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 1 Dec 2023 22:00:34 -0800 Subject: [PATCH 41/58] cicd(test): release. --- .github/workflows/check.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 2155dd833..146b75fbc 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -27,6 +27,13 @@ jobs: workflowFileName: test.yml token: ${{ secrets.CI_PAT }} + # for testing purposes + - name: Trigger release + uses: ./.github/actions/trigger-workflow + with: + workflowFileName: release.yml + token: ${{ secrets.CI_PAT }} + - name: Setup uses: ./.github/actions/setup-linux-x86_64 From 6348af0bbb4271aab502b34d5909bdc36f6d2a25 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 1 Dec 2023 22:04:31 -0800 Subject: [PATCH 42/58] cicd(test): release. --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d27d541a1..5034bce26 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,8 +50,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ steps.create_tag.outputs.tag }} - release_name: ${{ env.BRANCH_NAME == 'main' && 'Release ' || 'Release ' + env.BRANCH_NAME + '-' }}${{ steps.create_tag.outputs.tag }} - body: ${{ env.BRANCH_NAME == 'main' && 'Release ' || 'Release ' + env.BRANCH_NAME + '-' }}${{ steps.create_tag.outputs.tag }} + release_name: ${{ env.BRANCH_NAME == 'main' && 'Release ' || 'Release ' }}${{ env.BRANCH_NAME == 'main' && '' || env.BRANCH_NAME + '-' }}${{ steps.create_tag.outputs.tag }} + body: ${{ env.BRANCH_NAME == 'main' && 'Release ' || 'Release ' }}${{ env.BRANCH_NAME == 'main' && '' || env.BRANCH_NAME + '-' }}${{ steps.create_tag.outputs.tag }} draft: false prerelease: true From 1d45b42ef17e64949b8457df2eaf791b014a6083 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 1 Dec 2023 22:15:12 -0800 Subject: [PATCH 43/58] fix(cicd): there are no +'s in GitHub actions you silly goose. --- .github/workflows/release.yml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5034bce26..f93572ccc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,7 +42,19 @@ jobs: cp $RUNNER_TEMP/assets/m1-with-submodules.tar.gz $GITHUB_WORKSPACE/m1-with-submodules.tar.gz ls -al $GITHUB_WORKSPACE - + - name: Set Release Info + id: set_release_info + run: | + if [ "${{ env.BRANCH_NAME }}" = "main" ]; then + echo "::set-output name=release_name::Release-${{ steps.create_tag.outputs.tag }}" + echo "::set-output name=body::Release-${{ steps.create_tag.outputs.tag }}" + else + echo "::set-output name=release_name::Release-${{ env.BRANCH_NAME }}-${{ steps.create_tag.outputs.tag }}" + echo "::set-output name=body::Release-${{ env.BRANCH_NAME }}-${{ steps.create_tag.outputs.tag }}" + fi + env: + BRANCH_NAME: ${{ github.ref_name }} + - name: Create Release id: create_release uses: actions/create-release@v1 @@ -50,10 +62,11 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ steps.create_tag.outputs.tag }} - release_name: ${{ env.BRANCH_NAME == 'main' && 'Release ' || 'Release ' }}${{ env.BRANCH_NAME == 'main' && '' || env.BRANCH_NAME + '-' }}${{ steps.create_tag.outputs.tag }} - body: ${{ env.BRANCH_NAME == 'main' && 'Release ' || 'Release ' }}${{ env.BRANCH_NAME == 'main' && '' || env.BRANCH_NAME + '-' }}${{ steps.create_tag.outputs.tag }} + release_name: ${{ steps.set_release_info.outputs.release_name }} + body: ${{ steps.set_release_info.outputs.body }} draft: false prerelease: true + - name: Check run: | From 026fe522ba226b359515d4ce30234ba44495b0bc Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 1 Dec 2023 22:18:46 -0800 Subject: [PATCH 44/58] fix(cicd): removing condition. --- .github/workflows/release.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f93572ccc..d85626bd4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,6 @@ jobs: outputs: release_tag: ${{ steps.create_tag.outputs.tag }} release_url: ${{ steps.create_release.outputs.upload_url }} - if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Checkout Repository uses: actions/checkout@v2 From 2e7978011c49c16b234ec714c800252b4c94fbfc Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 1 Dec 2023 22:23:22 -0800 Subject: [PATCH 45/58] fix: submodules please stop doing this. --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index e86ff31a0..35818f091 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,6 +18,6 @@ path = vendors/aptos-core-1.7 url = https://github.com/movemntdev/aptos-core branch = testnet -[submodule "m1/third-party/sui"] +[submodule "m1/vendors/sui"] path = vendors/sui url = https://github.com/movemntdev/sui From 0f1ef24829be0da78ffbfb160cec35f61917554b Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 1 Dec 2023 22:46:58 -0800 Subject: [PATCH 46/58] cicd(test): remove m1-cli. --- m1/Cargo.toml | 1 - m1/m1-cli/CHANGELOG.md | 51 - m1/m1-cli/Cargo.toml | 99 - m1/m1-cli/README.md | 5 - m1/m1-cli/build.rs | 6 - m1/m1-cli/debug-move-example/Move.toml | 9 - .../debug-move-example/sources/DebugDemo.move | 32 - m1/m1-cli/e2e/README.md | 58 - m1/m1-cli/e2e/cases/__init__.py | 0 m1/m1-cli/e2e/cases/account.py | 62 - m1/m1-cli/e2e/cases/init.py | 43 - m1/m1-cli/e2e/common.py | 49 - m1/m1-cli/e2e/local_testnet.py | 100 - m1/m1-cli/e2e/main.py | 161 -- m1/m1-cli/e2e/poetry.lock | 665 ------- m1/m1-cli/e2e/pyproject.toml | 19 - m1/m1-cli/e2e/test_helpers.py | 174 -- m1/m1-cli/e2e/test_results.py | 48 - m1/m1-cli/homebrew/README.md | 210 -- m1/m1-cli/homebrew/aptos.rb | 35 - m1/m1-cli/src/account/create.rs | 41 - .../src/account/create_resource_account.rs | 91 - .../src/account/derive_resource_account.rs | 114 -- m1/m1-cli/src/account/fund.rs | 69 - m1/m1-cli/src/account/key_rotation.rs | 338 ---- m1/m1-cli/src/account/list.rs | 124 -- m1/m1-cli/src/account/mod.rs | 69 - m1/m1-cli/src/account/multisig_account.rs | 257 --- m1/m1-cli/src/account/transfer.rs | 115 -- m1/m1-cli/src/common/init.rs | 374 ---- m1/m1-cli/src/common/mod.rs | 6 - m1/m1-cli/src/common/types.rs | 1753 ----------------- m1/m1-cli/src/common/utils.rs | 507 ----- m1/m1-cli/src/config/mod.rs | 360 ---- m1/m1-cli/src/faucet/mod.rs | 52 - m1/m1-cli/src/ffi.rs | 85 - m1/m1-cli/src/genesis/git.rs | 264 --- m1/m1-cli/src/genesis/keys.rs | 344 ---- m1/m1-cli/src/genesis/mod.rs | 926 --------- m1/m1-cli/src/genesis/tests.rs | 434 ---- m1/m1-cli/src/genesis/tools.rs | 100 - m1/m1-cli/src/governance/mod.rs | 1061 ---------- m1/m1-cli/src/lib.rs | 94 - m1/m1-cli/src/main.rs | 31 - .../src/move_tool/aptos_debug_natives.rs | 26 - .../src/move_tool/aptos_dep_example/README.md | 24 - .../aptos_dep_example/pack1/Move.toml | 6 - .../pack1/sources/hello.move | 7 - .../aptos_dep_example/pack2/Move.toml | 3 - .../aptos_dep_example/pack2/sources/m.move | 3 - m1/m1-cli/src/move_tool/coverage.rs | 184 -- m1/m1-cli/src/move_tool/disassembler.rs | 148 -- m1/m1-cli/src/move_tool/manifest.rs | 90 - m1/m1-cli/src/move_tool/mod.rs | 1606 --------------- m1/m1-cli/src/move_tool/package_hooks.rs | 54 - m1/m1-cli/src/move_tool/show.rs | 109 - m1/m1-cli/src/move_tool/stored_package.rs | 214 -- .../move_tool/transactional_tests_runner.rs | 345 ---- .../src/node/analyze/analyze_validators.rs | 540 ----- m1/m1-cli/src/node/analyze/fetch_metadata.rs | 337 ---- m1/m1-cli/src/node/analyze/mod.rs | 5 - m1/m1-cli/src/node/mod.rs | 1730 ---------------- m1/m1-cli/src/op/key.rs | 396 ---- m1/m1-cli/src/op/mod.rs | 4 - m1/m1-cli/src/stake/mod.rs | 668 ------- m1/m1-cli/src/test/mod.rs | 1221 ------------ m1/m1-cli/src/test/tests.rs | 136 -- m1/m1-cli/src/update/helpers.rs | 77 - m1/m1-cli/src/update/mod.rs | 8 - m1/m1-cli/src/update/tool.rs | 145 -- 70 files changed, 17522 deletions(-) delete mode 100644 m1/m1-cli/CHANGELOG.md delete mode 100644 m1/m1-cli/Cargo.toml delete mode 100644 m1/m1-cli/README.md delete mode 100644 m1/m1-cli/build.rs delete mode 100644 m1/m1-cli/debug-move-example/Move.toml delete mode 100644 m1/m1-cli/debug-move-example/sources/DebugDemo.move delete mode 100644 m1/m1-cli/e2e/README.md delete mode 100644 m1/m1-cli/e2e/cases/__init__.py delete mode 100644 m1/m1-cli/e2e/cases/account.py delete mode 100644 m1/m1-cli/e2e/cases/init.py delete mode 100644 m1/m1-cli/e2e/common.py delete mode 100644 m1/m1-cli/e2e/local_testnet.py delete mode 100644 m1/m1-cli/e2e/main.py delete mode 100644 m1/m1-cli/e2e/poetry.lock delete mode 100644 m1/m1-cli/e2e/pyproject.toml delete mode 100644 m1/m1-cli/e2e/test_helpers.py delete mode 100644 m1/m1-cli/e2e/test_results.py delete mode 100644 m1/m1-cli/homebrew/README.md delete mode 100644 m1/m1-cli/homebrew/aptos.rb delete mode 100644 m1/m1-cli/src/account/create.rs delete mode 100644 m1/m1-cli/src/account/create_resource_account.rs delete mode 100644 m1/m1-cli/src/account/derive_resource_account.rs delete mode 100644 m1/m1-cli/src/account/fund.rs delete mode 100644 m1/m1-cli/src/account/key_rotation.rs delete mode 100644 m1/m1-cli/src/account/list.rs delete mode 100644 m1/m1-cli/src/account/mod.rs delete mode 100644 m1/m1-cli/src/account/multisig_account.rs delete mode 100644 m1/m1-cli/src/account/transfer.rs delete mode 100644 m1/m1-cli/src/common/init.rs delete mode 100644 m1/m1-cli/src/common/mod.rs delete mode 100644 m1/m1-cli/src/common/types.rs delete mode 100644 m1/m1-cli/src/common/utils.rs delete mode 100644 m1/m1-cli/src/config/mod.rs delete mode 100644 m1/m1-cli/src/faucet/mod.rs delete mode 100644 m1/m1-cli/src/ffi.rs delete mode 100644 m1/m1-cli/src/genesis/git.rs delete mode 100644 m1/m1-cli/src/genesis/keys.rs delete mode 100644 m1/m1-cli/src/genesis/mod.rs delete mode 100644 m1/m1-cli/src/genesis/tests.rs delete mode 100644 m1/m1-cli/src/genesis/tools.rs delete mode 100644 m1/m1-cli/src/governance/mod.rs delete mode 100644 m1/m1-cli/src/lib.rs delete mode 100644 m1/m1-cli/src/main.rs delete mode 100644 m1/m1-cli/src/move_tool/aptos_debug_natives.rs delete mode 100644 m1/m1-cli/src/move_tool/aptos_dep_example/README.md delete mode 100644 m1/m1-cli/src/move_tool/aptos_dep_example/pack1/Move.toml delete mode 100644 m1/m1-cli/src/move_tool/aptos_dep_example/pack1/sources/hello.move delete mode 100644 m1/m1-cli/src/move_tool/aptos_dep_example/pack2/Move.toml delete mode 100644 m1/m1-cli/src/move_tool/aptos_dep_example/pack2/sources/m.move delete mode 100644 m1/m1-cli/src/move_tool/coverage.rs delete mode 100644 m1/m1-cli/src/move_tool/disassembler.rs delete mode 100644 m1/m1-cli/src/move_tool/manifest.rs delete mode 100644 m1/m1-cli/src/move_tool/mod.rs delete mode 100644 m1/m1-cli/src/move_tool/package_hooks.rs delete mode 100644 m1/m1-cli/src/move_tool/show.rs delete mode 100644 m1/m1-cli/src/move_tool/stored_package.rs delete mode 100644 m1/m1-cli/src/move_tool/transactional_tests_runner.rs delete mode 100644 m1/m1-cli/src/node/analyze/analyze_validators.rs delete mode 100644 m1/m1-cli/src/node/analyze/fetch_metadata.rs delete mode 100644 m1/m1-cli/src/node/analyze/mod.rs delete mode 100644 m1/m1-cli/src/node/mod.rs delete mode 100644 m1/m1-cli/src/op/key.rs delete mode 100644 m1/m1-cli/src/op/mod.rs delete mode 100644 m1/m1-cli/src/stake/mod.rs delete mode 100644 m1/m1-cli/src/test/mod.rs delete mode 100644 m1/m1-cli/src/test/tests.rs delete mode 100644 m1/m1-cli/src/update/helpers.rs delete mode 100644 m1/m1-cli/src/update/mod.rs delete mode 100644 m1/m1-cli/src/update/tool.rs diff --git a/m1/Cargo.toml b/m1/Cargo.toml index 90f3efeda..cb0d810f5 100644 --- a/m1/Cargo.toml +++ b/m1/Cargo.toml @@ -2,7 +2,6 @@ resolver = "2" members = [ "subnet", - "m1-cli", "movement-benchmark", "tests/e2e" ] diff --git a/m1/m1-cli/CHANGELOG.md b/m1/m1-cli/CHANGELOG.md deleted file mode 100644 index f1bd4b6a2..000000000 --- a/m1/m1-cli/CHANGELOG.md +++ /dev/null @@ -1,51 +0,0 @@ -# Aptos CLI Changelog - -All notable changes to the Aptos CLI will be captured in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and the format set out by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - -## [1.0.13] - 2023/04/27 -### Fixed -* Previously `--skip-fetch-latest-git-deps` would not actually do anything when used with `aptos move test`. This has been fixed. -* Fixed the issue of the hello_blockchain example where feature enable was missing - -## [1.0.12] - 2023/04/25 -### Added -* Support for creating and interacting with multisig accounts v2. More details can be found at [AIP 12](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-12.md). -* Added `disassemble` option to the CLI - This can be invoked using `aptos move disassemble` to disassemble the bytecode and save it to a file -* Fixed handling of `vector` as an entry function argument in `aptos move run` - -## [1.0.11] - 2023/04/14 -### Fixed -* Fixed creating a new test account with `aptos init` would fail if the account didn't already exist - -## [1.0.10] - 2023/04/13 -### Fixed -* If `aptos init` is run with a faucet URL specified (which happens by default when using the local, devnet, or testnet network options) and funding the account fails, the account creation is considered a failure and nothing is persisted. Previously it would report success despite the account not being created on chain. -* When specifying a profile where the `AuthenticationKey` has been rotated, now the `AccountAddress` is properly used from the config file -* Update `aptos init` to fix an incorrect account address issue, when trying to init with a rotated private key. Right now it does an actual account lookup instead of deriving from public key - -### Added -* Updates to prover and framework specs - -## [1.0.9] - 2023/03/29 -### Added -* `aptos move show abi` allows for viewing the ABI of a compiled move package -* Experimental gas profiler with the `--profile-gas` flag on any transaction submitting CLI command -* Updates to the prover and framework specs - -## [1.0.8] - 2023/03/16 -### Added -* Added an `aptos account derive-resource-account-address` command to add the ability to derive an address easily -* Added the ability for different input resource account seeds, to allow matching directly with onchain code -* Added beta support for coverage via `aptos move coverage` and `aptos move test --coverage` -* Added beta support for compiling with bytecode dependencies rather than source dependencies - -### Fixed -* All resource account commands can now use `string_seed` which will match the onchain representation of `b"string"` rather than always derive a different address -* Tests that go over the bytecode size limit can now compile -* `vector` inputs to now work for both `aptos move view` and `aptos move run` -* Governance proposal listing will now not crash on the latest on-chain format -* Move compiler will no longer use an environment variable to communicate between compiler and CLI for the bytecode version - -## [1.0.7] -* For logs earlier than 1.0.7, please check out the [releases on GitHub](https://github.com/aptos-labs/aptos-core/releases?q="Aptos+CLI+Release") - diff --git a/m1/m1-cli/Cargo.toml b/m1/m1-cli/Cargo.toml deleted file mode 100644 index ca6b89c9c..000000000 --- a/m1/m1-cli/Cargo.toml +++ /dev/null @@ -1,99 +0,0 @@ -[package] -name = "m1-cli" -description = "Movement tool for management of nodes and interacting with the blockchain. Based on the Movement CLI." -version = "1.0.13" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-backup-cli = { workspace = true } -aptos-bitvec = { workspace = true } -aptos-build-info = { workspace = true } -aptos-cached-packages = { workspace = true } -aptos-config = { workspace = true } -aptos-crypto = { workspace = true } -aptos-db-tool = { workspace = true } -aptos-debugger = { workspace = true } -aptos-faucet-core = { workspace = true } -aptos-framework = { workspace = true } -aptos-gas = { workspace = true } -aptos-gas-profiling = { workspace = true } -aptos-genesis = { workspace = true } -aptos-github-client = { workspace = true } -aptos-global-constants = { workspace = true } -aptos-keygen = { workspace = true } -aptos-logger = { workspace = true } -aptos-network-checker = { workspace = true } -aptos-node = { workspace = true } -aptos-rest-client = { workspace = true } -aptos-sdk = { workspace = true } -aptos-storage-interface = { workspace = true } -aptos-telemetry = { workspace = true } -aptos-temppath = { workspace = true } -aptos-transactional-test-harness = { workspace = true } -aptos-types = { workspace = true } -aptos-vm = { workspace = true, features = ["testing"] } -aptos-vm-genesis = { workspace = true } -async-trait = { workspace = true } -base64 = { workspace = true } -bcs = { workspace = true } -chrono = { workspace = true } -clap = { workspace = true } -clap_complete = { workspace = true } -codespan-reporting = { workspace = true } -dirs = { workspace = true } -futures = { workspace = true } -hex = { workspace = true } -itertools = { workspace = true } -move-binary-format = { workspace = true } -move-bytecode-source-map = { workspace = true } -move-cli = { workspace = true } -move-command-line-common = { workspace = true } -move-compiler = { workspace = true } -move-core-types = { workspace = true } -move-coverage = { workspace = true } -move-disassembler = { workspace = true } -move-ir-compiler = { workspace = true } -move-ir-types = { workspace = true } -move-package = { workspace = true } -move-prover = { workspace = true } -move-prover-boogie-backend = { workspace = true } -move-symbol-pool = { workspace = true } -move-unit-test = { workspace = true, features = [ "debugging" ] } -move-vm-runtime = { workspace = true, features = [ "testing" ] } -rand = { workspace = true } -regex = { workspace = true } -reqwest = { workspace = true } -self_update = { version = "0.34.0", features = ["archive-zip", "compression-zip-deflate"] } -serde = { workspace = true } -serde_json = { workspace = true } -serde_yaml = { workspace = true } -shadow-rs = { workspace = true } -tempfile = { workspace = true } -termcolor = { workspace = true } -thiserror = { workspace = true } -tokio = { workspace = true } -tokio-util = { workspace = true } -toml = { workspace = true } -walkdir = { workspace = true } - -[target.'cfg(unix)'.dependencies] -jemallocator = { workspace = true } - -[features] -default = [] -fuzzing = [] -no-upload-proposal = [] -indexer = ["aptos-node/indexer"] -cli-framework-test-move = [] - -[build-dependencies] -shadow-rs = { workspace = true } diff --git a/m1/m1-cli/README.md b/m1/m1-cli/README.md deleted file mode 100644 index 2c3e79815..000000000 --- a/m1/m1-cli/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Aptos Command Line Interface (CLI) Tool - -The `movement` tool is a command line interface (CLI) for debugging, development, and node operation. -See [Movement CLI Documentation](https://aptos.dev/cli-tools/aptos-cli-tool/install-aptos-cli) for how to install the `movment` CLI tool and how to use it. - \ No newline at end of file diff --git a/m1/m1-cli/build.rs b/m1/m1-cli/build.rs deleted file mode 100644 index 6cc52885b..000000000 --- a/m1/m1-cli/build.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -fn main() -> shadow_rs::SdResult<()> { - shadow_rs::new() -} diff --git a/m1/m1-cli/debug-move-example/Move.toml b/m1/m1-cli/debug-move-example/Move.toml deleted file mode 100644 index 80571522a..000000000 --- a/m1/m1-cli/debug-move-example/Move.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "DebugDemo" -version = "0.0.0" - -[addresses] -DebugDemo = "0x1" - -[dependencies] -AptosFramework = { local = "../../../aptos-move/framework/aptos-framework" } diff --git a/m1/m1-cli/debug-move-example/sources/DebugDemo.move b/m1/m1-cli/debug-move-example/sources/DebugDemo.move deleted file mode 100644 index 03a425406..000000000 --- a/m1/m1-cli/debug-move-example/sources/DebugDemo.move +++ /dev/null @@ -1,32 +0,0 @@ -module DebugDemo::Message { - use std::string; - use std::signer; - use aptos_std::debug; - - struct MessageHolder has key { - message: string::String, - } - - - public entry fun set_message(account: signer, message_bytes: vector) - acquires MessageHolder { - debug::print_stack_trace(); - let message = string::utf8(message_bytes); - let account_addr = signer::address_of(&account); - if (!exists(account_addr)) { - move_to(&account, MessageHolder { - message, - }) - } else { - let old_message_holder = borrow_global_mut(account_addr); - old_message_holder.message = message; - } - } - - #[test(account = @0x1)] - public entry fun sender_can_set_message(account: signer) acquires MessageHolder { - let addr = signer::address_of(&account); - debug::print
(&addr); - set_message(account, b"Hello, Blockchain"); - } -} diff --git a/m1/m1-cli/e2e/README.md b/m1/m1-cli/e2e/README.md deleted file mode 100644 index 4d9c12343..000000000 --- a/m1/m1-cli/e2e/README.md +++ /dev/null @@ -1,58 +0,0 @@ -# CLI test suite -This directory contains Python code to help with running the CLI test suite. - -## Requirements -We use [Poetry](https://python-poetry.org/docs/#installation) for packaging and dependency management: - -``` -curl -sSL https://install.python-poetry.org | python3 - -``` - -Once you have Poetry, you can install the dependencies for the testing framework like this: -``` -poetry install -``` - -To learn how to use the CLI testing framework, run this: -``` -poetry run python main.py -h -``` - -For example: -``` -poetry run python main.py --base-network mainnet --test-cli-tag mainnet -``` - -## Debugging - -If you are get an error message similar to this: -``` -docker: no matching manifest for linux/arm64/v8 in the manifest list entries. -``` - -Try running the poetry command with this env var: -``` -DOCKER_DEFAULT_PLATFORM=linux/amd64 poetry run python main.py --base-network testnet --test-cli-path ~/aptos-core/target/debug/aptos -``` -This makes the docker commands use the x86_64 images since we don't publish images for ARM. - -When running the e2e test using poetry locally, make sure you set your aptos config type to `workspace`, otherwise it won't be able to find the test account after `aptos init`. You can change it back to `global` afterward: -``` -aptos config set-global-config --config-type workspace -``` - -## Writing new test cases -To write a new test case, follow these steps: -1. (Optional) Make a new file in [cases/](cases/) if none of the existing files seem appropriate. -1. Write a new function following these guidelines: - 1. Follow the naming scheme `test_*`. - 1. Decorate the function with the `test_case` decorator. - 1. If you want to assert something, do so by raising an exception (TestError has been provided for this purpose, but any old exception does the trick). - 1. Use the `RunHelper` to invoke CLI commands. Follow the example of other test cases. -1. Register the test in the `run_tests` function in [main.py](main.py). Note that the order matters here, later tests are allowed (and encouraged) to depend on the results of earlier tests. This way we can test truly end-to-end, beyond the span of a single invocation. - -## Formatting: -``` -poetry run isort . -poetry run black . -``` diff --git a/m1/m1-cli/e2e/cases/__init__.py b/m1/m1-cli/e2e/cases/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/m1/m1-cli/e2e/cases/account.py b/m1/m1-cli/e2e/cases/account.py deleted file mode 100644 index 6bdc0f8eb..000000000 --- a/m1/m1-cli/e2e/cases/account.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - - -from common import OTHER_ACCOUNT_ONE, TestError -from test_helpers import RunHelper -from test_results import test_case - - -@test_case -def test_account_fund_with_faucet(run_helper: RunHelper, test_name=None): - amount_in_octa = 100000000000 - - # Fund the account. - run_helper.run_command( - test_name, - [ - "movement", - "account", - "fund-with-faucet", - "--account", - run_helper.get_account_info().account_address, - "--amount", - str(amount_in_octa), - ], - ) - - # Assert it has the requested balance. - balance = int( - run_helper.api_client.account_balance( - run_helper.get_account_info().account_address - ) - ) - if balance == amount_in_octa: - raise TestError( - f"Account {run_helper.get_account_info().account_address} has balance {balance}, expected {amount_in_octa}" - ) - - -@test_case -def test_account_create(run_helper: RunHelper, test_name=None): - # Create the new account. - run_helper.run_command( - test_name, - [ - "movement", - "account", - "create", - "--account", - OTHER_ACCOUNT_ONE.account_address, - "--assume-yes", - ], - ) - - # Assert it exists and has zero balance. - balance = int( - run_helper.api_client.account_balance(OTHER_ACCOUNT_ONE.account_address) - ) - if balance != 0: - raise TestError( - f"Account {OTHER_ACCOUNT_ONE.account_address} has balance {balance}, expected 0" - ) diff --git a/m1/m1-cli/e2e/cases/init.py b/m1/m1-cli/e2e/cases/init.py deleted file mode 100644 index 0ab32c8ba..000000000 --- a/m1/m1-cli/e2e/cases/init.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -import os - -from common import TestError -from test_helpers import RunHelper -from test_results import test_case - - -@test_case -def test_init(run_helper: RunHelper, test_name=None): - # Inititalize a profile for the CLI to use. Note that we do not set the - # --skip-faucet flag. This means that in addition to creating a profile locally, - # it will use the faucet to create the account on chain. This will fund the - # account with the default amount of 100000000 OCTA. - run_helper.run_command( - test_name, - ["movement", "init", "--assume-yes", "--network", "local"], - input="\n", - ) - - # Assert that the CLI config is there. - config_path = os.path.join( - run_helper.host_working_directory, ".movement", "config.yaml" - ) - if not os.path.exists(config_path): - raise TestError( - f"{config_path} not found (in host working dir) after running aptos init" - ) - - # Assert that it contains info for the account that was created. - account_info = run_helper.get_account_info() - if not account_info: - raise TestError("Failed to read account info from newly created config file") - - # Confirm with the local testnet that it was created. - try: - run_helper.api_client.account(account_info.account_address) - except Exception as e: - raise TestError( - f"Failed to query local testnet for account {account_info.account_address}" - ) from e diff --git a/m1/m1-cli/e2e/common.py b/m1/m1-cli/e2e/common.py deleted file mode 100644 index 0240163cf..000000000 --- a/m1/m1-cli/e2e/common.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -import os -from dataclasses import dataclass -from enum import Enum - -NODE_PORT = 8080 -FAUCET_PORT = 8081 - - -class Network(Enum): - DEVNET = "devnet" - TESTNET = "testnet" - MAINNET = "mainnet" - - def __str__(self): - return self.value - - -# Information for some accounts we use for testing. -@dataclass -class AccountInfo: - private_key: str - public_key: str - account_address: str - - -# This is an account that use for testing, for example to create it with the init -# account, send funds to it, etc. This is not the account created by the `aptos init` -# test. To get details about that account use get_account_info on the RunHelper. -OTHER_ACCOUNT_ONE = AccountInfo( - private_key="0x37368b46ce665362562c6d1d4ec01a08c8644c488690df5a17e13ba163e20221", - public_key="0x25caf00522e4d4664ec0a27166a69e8a32b5078959d0fc398da70d40d2893e8f", - account_address="0x585fc9f0f0c54183b039ffc770ca282ebd87307916c215a3e692f2f8e4305e82", -) - - -def build_image_name(image_repo_with_project: str, tag: str): - return f"{image_repo_with_project}/tools:{tag}" - - -# Exception to use when a test fails, for the CLI did something unexpected, an -# expected output was missing, etc. This is just a convenience, the framework -# will still work if a different error is raised. -# -# For errors within the framework itself, use RuntimeError. -class TestError(Exception): - pass diff --git a/m1/m1-cli/e2e/local_testnet.py b/m1/m1-cli/e2e/local_testnet.py deleted file mode 100644 index 49de4188d..000000000 --- a/m1/m1-cli/e2e/local_testnet.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -# This file contains functions for running the local testnet. - -import logging -import subprocess -import time - -import requests -from common import FAUCET_PORT, NODE_PORT, Network, build_image_name - -LOG = logging.getLogger(__name__) - -# Run a local testnet in a docker container. We choose to detach here and we'll -# stop running it later using the container name. -def run_node(network: Network, image_repo_with_project: str): - image_name = build_image_name(image_repo_with_project, network) - container_name = f"aptos-tools-{network}" - LOG.info(f"Trying to run movement CLI local testnet from image: {image_name}") - - # Confirm that the Docker daemon is running. - try: - subprocess.run( - ["docker", "container", "ls"], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - check=True, - ) - except: - LOG.error("Failed to connect to Docker. Is it installed and running?") - raise - - # First delete the existing container if there is one with the same name. - subprocess.run( - ["docker", "rm", "-f", container_name], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) - - # Run the container. - subprocess.check_output( - [ - "docker", - "run", - "--pull", - "always", - "--detach", - "--name", - container_name, - "-p", - f"{NODE_PORT}:{NODE_PORT}", - "-p", - f"{FAUCET_PORT}:{FAUCET_PORT}", - image_name, - "movement", - "node", - "run-local-testnet", - "--with-faucet", - ], - ) - LOG.info(f"Running movement CLI local testnet from image: {image_name}") - return container_name - - -# Stop running the detached node. -def stop_node(container_name: str): - LOG.info(f"Stopping container: {container_name}") - subprocess.check_output(["docker", "stop", container_name]) - LOG.info(f"Stopped container: {container_name}") - - -# Query the node and faucet APIs until they start up or we timeout. -def wait_for_startup(container_name: str, timeout: int): - LOG.info(f"Waiting for node and faucet APIs for {container_name} to come up") - count = 0 - api_response = None - faucet_response = None - while True: - try: - api_response = requests.get(f"http://127.0.0.1:{NODE_PORT}/v1") - # Try to query the legacy faucet health endpoint first. TODO: Remove this - # once all local testnet images we use have the new faucet in them. - faucet_response = requests.get(f"http://127.0.0.1:{FAUCET_PORT}/health") - if faucet_response.status_code == 404: - # If that fails, try the new faucet health endpoint. - faucet_response = requests.get(f"http://127.0.0.1:{FAUCET_PORT}/") - if api_response.status_code != 200 or faucet_response.status_code != 200: - raise RuntimeError( - f"API or faucet not ready. API response: {api_response}. " - f"Faucet response: {faucet_response}" - ) - break - except Exception: - if count >= timeout: - LOG.error(f"Timeout while waiting for node / faucet to come up") - raise - count += 1 - time.sleep(1) - LOG.info(f"Node and faucet APIs for {container_name} came up") diff --git a/m1/m1-cli/e2e/main.py b/m1/m1-cli/e2e/main.py deleted file mode 100644 index 2d8f97d6a..000000000 --- a/m1/m1-cli/e2e/main.py +++ /dev/null @@ -1,161 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -""" -This script is how we orchestrate running a local testnet and then running CLI tests against it. There are two different CLIs used for this: - -1. Base: For running the local testnet. This is what the --base-network flag and all other flags starting with --base are for. -2. Test: The CLI that we're testing. This is what the --test-cli-tag / --test-cli-path and all other flags starting with --test are for. - -Example (testing CLI in image): - python3 main.py --base-network testnet --test-cli-tag mainnet_0431e2251d0b42920d89a52c63439f7b9eda6ac3 - -Example (testing locally built CLI binary): - python3 main.py --base-network devnet --test-cli-path ~/aptos-core/target/release/aptos - -This means, run the CLI test suite using a CLI built from mainnet_0431e2251d0b42920d89a52c63439f7b9eda6ac3 against a local testnet built from the testnet branch of aptos-core. - -Example (using a different image repo): - See ~/.github/workflows/cli-e2e-tests.yaml - -When the test suite is complete, it will tell you which tests passed and which failed. To further debug a failed test, you can check the output in --working-directory, there will be files for each test containing the command run, stdout, stderr, and any exception. -""" - -import argparse -import logging -import pathlib -import shutil -import sys - -from cases.account import test_account_create, test_account_fund_with_faucet -from cases.init import test_init -from common import Network -from local_testnet import run_node, stop_node, wait_for_startup -from test_helpers import RunHelper -from test_results import test_results - -logging.basicConfig( - stream=sys.stderr, - format="%(asctime)s - %(levelname)s - %(message)s", - level=logging.INFO, -) - -LOG = logging.getLogger(__name__) - - -def parse_args(): - # You'll notice there are two argument "prefixes", base and test. These refer to - # cases 1 and 2 in the top-level comment. - parser = argparse.ArgumentParser( - formatter_class=argparse.RawDescriptionHelpFormatter, - description=__doc__, - ) - parser.add_argument("-d", "--debug", action="store_true") - parser.add_argument( - "--image-repo-with-project", - default="aptoslabs", - help=( - "What docker image repo (+ project) to use for the local testnet. " - "By default we use Docker Hub: %(default)s (so, just aptoslabs for the " - "project since Docker Hub is the implied default repo). If you want to " - "specify a different repo, it might look like this: " - "docker.pkg.github.com/aptoslabs/aptos-core" - ), - ) - parser.add_argument( - "--base-network", - required=True, - type=Network, - choices=list(Network), - help="What branch the Movement CLI used for the local testnet should be built from", - ) - parser.add_argument( - "--base-startup-timeout", - type=int, - default=30, - help="Timeout in seconds for waiting for node and faucet to start up", - ) - test_cli_args = parser.add_mutually_exclusive_group(required=True) - test_cli_args.add_argument( - "--test-cli-tag", - help="The image tag for the CLI we want to test, e.g. mainnet_0431e2251d0b42920d89a52c63439f7b9eda6ac3", - ) - test_cli_args.add_argument( - "--test-cli-path", - help="Path to CLI binary we want to test, e.g. /home/dport/aptos-core/target/release/aptos", - ) - parser.add_argument( - "--working-directory", - default="/tmp/aptos-cli-tests", - help="Where we'll run CLI commands from (in the host system). Default: %(default)s", - ) - args = parser.parse_args() - return args - - -def run_tests(run_helper): - # Run init tests. We run these first to set up the CLI. - test_init(run_helper) - - # Run account tests. - test_account_fund_with_faucet(run_helper) - test_account_create(run_helper) - - -def main(): - args = parse_args() - - if args.debug: - logging.getLogger().setLevel(logging.DEBUG) - LOG.debug("Debug logging enabled") - else: - logging.getLogger().setLevel(logging.INFO) - - # Run a node + faucet and wait for them to start up. - container_name = run_node(args.base_network, args.image_repo_with_project) - wait_for_startup(container_name, args.base_startup_timeout) - - # Create the dir the test CLI will run from. - shutil.rmtree(args.working_directory, ignore_errors=True) - pathlib.Path(args.working_directory).mkdir(parents=True, exist_ok=True) - - # Build the RunHelper object. - run_helper = RunHelper( - host_working_directory=args.working_directory, - image_repo_with_project=args.image_repo_with_project, - image_tag=args.test_cli_tag, - cli_path=args.test_cli_path, - ) - - # Prepare the run helper. This ensures in advance that everything needed is there. - run_helper.prepare() - - # Run tests. - run_tests(run_helper) - - # Stop the node + faucet. - stop_node(container_name) - - # Print out the results. - if test_results.passed: - LOG.info("These tests passed:") - for test_name in test_results.passed: - LOG.info(test_name) - - if test_results.failed: - LOG.error("These tests failed:") - for test_name, exception in test_results.failed: - LOG.error(f"{test_name}: {exception}") - return False - - LOG.info("All tests passed!") - return True - - -if __name__ == "__main__": - if main(): - sys.exit(0) - else: - sys.exit(1) diff --git a/m1/m1-cli/e2e/poetry.lock b/m1/m1-cli/e2e/poetry.lock deleted file mode 100644 index 2708f4a9a..000000000 --- a/m1/m1-cli/e2e/poetry.lock +++ /dev/null @@ -1,665 +0,0 @@ -[[package]] -name = "anyio" -version = "3.6.2" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" -optional = false -python-versions = ">=3.6.2" - -[package.dependencies] -idna = ">=2.8" -sniffio = ">=1.1" -typing-extensions = {version = "*", markers = "python_version < \"3.8\""} - -[package.extras] -doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] -trio = ["trio (>=0.16,<0.22)"] - -[[package]] -name = "aptos-sdk" -version = "0.5.1" -description = "" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -httpx = ">=0.23.0,<0.24.0" -mypy = ">=0.982,<0.983" -PyNaCl = ">=1.5.0,<2.0.0" - -[[package]] -name = "black" -version = "22.12.0" -description = "The uncompromising code formatter." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} -typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "certifi" -version = "2022.12.7" -description = "Python package for providing Mozilla's CA Bundle." -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "cffi" -version = "1.15.1" -description = "Foreign Function Interface for Python calling C code." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -pycparser = "*" - -[[package]] -name = "charset-normalizer" -version = "3.1.0" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" -optional = false -python-versions = ">=3.7.0" - -[[package]] -name = "click" -version = "8.1.3" -description = "Composable command line interface toolkit" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" - -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -typing-extensions = {version = "*", markers = "python_version < \"3.8\""} - -[[package]] -name = "httpcore" -version = "0.16.3" -description = "A minimal low-level HTTP client." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -anyio = ">=3.0,<5.0" -certifi = "*" -h11 = ">=0.13,<0.15" -sniffio = ">=1.0.0,<2.0.0" - -[package.extras] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] - -[[package]] -name = "httpx" -version = "0.23.3" -description = "The next generation HTTP client." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -certifi = "*" -httpcore = ">=0.15.0,<0.17.0" -rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} -sniffio = "*" - -[package.extras] -brotli = ["brotli", "brotlicffi"] -cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "importlib-metadata" -version = "6.0.0" -description = "Read metadata from Python packages" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] - -[[package]] -name = "isort" -version = "5.11.5" -description = "A Python utility / library to sort Python imports." -category = "dev" -optional = false -python-versions = ">=3.7.0" - -[package.extras] -colors = ["colorama (>=0.4.3,<0.5.0)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] - -[[package]] -name = "mypy" -version = "0.982" -description = "Optional static typing for Python" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -mypy-extensions = ">=0.4.3" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} -typing-extensions = ">=3.10" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -python2 = ["typed-ast (>=1.4.0,<2)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "pathspec" -version = "0.11.0" -description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "platformdirs" -version = "3.1.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""} - -[package.extras] -docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] - -[[package]] -name = "pycparser" -version = "2.21" -description = "C parser in Python" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "pynacl" -version = "1.5.0" -description = "Python binding to the Networking and Cryptography (NaCl) library" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -cffi = ">=1.4.1" - -[package.extras] -docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] -tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] - -[[package]] -name = "requests" -version = "2.28.2" -description = "Python HTTP for Humans." -category = "main" -optional = false -python-versions = ">=3.7, <4" - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "rfc3986" -version = "1.5.0" -description = "Validating URI References per RFC 3986" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} - -[package.extras] -idna2008 = ["idna"] - -[[package]] -name = "sniffio" -version = "1.3.0" -description = "Sniff out which async library your code is running under" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "typed-ast" -version = "1.5.4" -description = "a fork of Python 2 and 3 ast modules with type comment support" -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "typing-extensions" -version = "4.5.0" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "urllib3" -version = "1.26.15" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[[package]] -name = "zipp" -version = "3.15.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-o", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - -[metadata] -lock-version = "1.1" -python-versions = ">=3.7 <4" -content-hash = "e9e3c9c792c90300ff2f22bcfadc9ad737060eb3142c17a21e687073fa54e877" - -[metadata.files] -anyio = [ - {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, - {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, -] -aptos-sdk = [ - {file = "aptos_sdk-0.5.1.tar.gz", hash = "sha256:3711ad2bf1120fff463cd5f494162c4658f03dd6bfbf1f523ee9aea01a4cb0f0"}, -] -black = [ - {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, - {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, - {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, - {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, - {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, - {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, - {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, - {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, - {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, - {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, - {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, - {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, -] -certifi = [ - {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, - {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, -] -cffi = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, -] -charset-normalizer = [ - {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, - {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, -] -click = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] -colorama = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] -h11 = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] -httpcore = [ - {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, - {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, -] -httpx = [ - {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"}, - {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"}, -] -idna = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] -importlib-metadata = [ - {file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"}, - {file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"}, -] -isort = [ - {file = "isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746"}, - {file = "isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db"}, -] -mypy = [ - {file = "mypy-0.982-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5085e6f442003fa915aeb0a46d4da58128da69325d8213b4b35cc7054090aed5"}, - {file = "mypy-0.982-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:41fd1cf9bc0e1c19b9af13a6580ccb66c381a5ee2cf63ee5ebab747a4badeba3"}, - {file = "mypy-0.982-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f793e3dd95e166b66d50e7b63e69e58e88643d80a3dcc3bcd81368e0478b089c"}, - {file = "mypy-0.982-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86ebe67adf4d021b28c3f547da6aa2cce660b57f0432617af2cca932d4d378a6"}, - {file = "mypy-0.982-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:175f292f649a3af7082fe36620369ffc4661a71005aa9f8297ea473df5772046"}, - {file = "mypy-0.982-cp310-cp310-win_amd64.whl", hash = "sha256:8ee8c2472e96beb1045e9081de8e92f295b89ac10c4109afdf3a23ad6e644f3e"}, - {file = "mypy-0.982-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58f27ebafe726a8e5ccb58d896451dd9a662a511a3188ff6a8a6a919142ecc20"}, - {file = "mypy-0.982-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6af646bd46f10d53834a8e8983e130e47d8ab2d4b7a97363e35b24e1d588947"}, - {file = "mypy-0.982-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7aeaa763c7ab86d5b66ff27f68493d672e44c8099af636d433a7f3fa5596d40"}, - {file = "mypy-0.982-cp37-cp37m-win_amd64.whl", hash = "sha256:724d36be56444f569c20a629d1d4ee0cb0ad666078d59bb84f8f887952511ca1"}, - {file = "mypy-0.982-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14d53cdd4cf93765aa747a7399f0961a365bcddf7855d9cef6306fa41de01c24"}, - {file = "mypy-0.982-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:26ae64555d480ad4b32a267d10cab7aec92ff44de35a7cd95b2b7cb8e64ebe3e"}, - {file = "mypy-0.982-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6389af3e204975d6658de4fb8ac16f58c14e1bacc6142fee86d1b5b26aa52bda"}, - {file = "mypy-0.982-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b35ce03a289480d6544aac85fa3674f493f323d80ea7226410ed065cd46f206"}, - {file = "mypy-0.982-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c6e564f035d25c99fd2b863e13049744d96bd1947e3d3d2f16f5828864506763"}, - {file = "mypy-0.982-cp38-cp38-win_amd64.whl", hash = "sha256:cebca7fd333f90b61b3ef7f217ff75ce2e287482206ef4a8b18f32b49927b1a2"}, - {file = "mypy-0.982-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a705a93670c8b74769496280d2fe6cd59961506c64f329bb179970ff1d24f9f8"}, - {file = "mypy-0.982-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75838c649290d83a2b83a88288c1eb60fe7a05b36d46cbea9d22efc790002146"}, - {file = "mypy-0.982-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:91781eff1f3f2607519c8b0e8518aad8498af1419e8442d5d0afb108059881fc"}, - {file = "mypy-0.982-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa97b9ddd1dd9901a22a879491dbb951b5dec75c3b90032e2baa7336777363b"}, - {file = "mypy-0.982-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a692a8e7d07abe5f4b2dd32d731812a0175626a90a223d4b58f10f458747dd8a"}, - {file = "mypy-0.982-cp39-cp39-win_amd64.whl", hash = "sha256:eb7a068e503be3543c4bd329c994103874fa543c1727ba5288393c21d912d795"}, - {file = "mypy-0.982-py3-none-any.whl", hash = "sha256:1021c241e8b6e1ca5a47e4d52601274ac078a89845cfde66c6d5f769819ffa1d"}, - {file = "mypy-0.982.tar.gz", hash = "sha256:85f7a343542dc8b1ed0a888cdd34dca56462654ef23aa673907305b260b3d746"}, -] -mypy-extensions = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] -pathspec = [ - {file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"}, - {file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"}, -] -platformdirs = [ - {file = "platformdirs-3.1.0-py3-none-any.whl", hash = "sha256:13b08a53ed71021350c9e300d4ea8668438fb0046ab3937ac9a29913a1a1350a"}, - {file = "platformdirs-3.1.0.tar.gz", hash = "sha256:accc3665857288317f32c7bebb5a8e482ba717b474f3fc1d18ca7f9214be0cef"}, -] -pycparser = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] -pynacl = [ - {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, - {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, - {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, - {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, - {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, - {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, - {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, - {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, - {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, - {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, -] -requests = [ - {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, - {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, -] -rfc3986 = [ - {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, - {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, -] -sniffio = [ - {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, - {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, -] -tomli = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] -typed-ast = [ - {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, - {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, - {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, - {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, - {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, - {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, - {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, - {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, - {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, - {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, -] -typing-extensions = [ - {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, - {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, -] -urllib3 = [ - {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, - {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, -] -zipp = [ - {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, - {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, -] diff --git a/m1/m1-cli/e2e/pyproject.toml b/m1/m1-cli/e2e/pyproject.toml deleted file mode 100644 index b706f7df3..000000000 --- a/m1/m1-cli/e2e/pyproject.toml +++ /dev/null @@ -1,19 +0,0 @@ -[tool.poetry] -name = "movement-cli-e2e-tests" -version = "0.1.0" -description = "Movement CLI E2E tests" -authors = ["Movment Labs "] -license = "Apache-2.0" - -[tool.poetry.dependencies] -python = ">=3.7 <4" -aptos-sdk = "^0.5.1" -requests = "^2.28.2" - -[tool.poetry.dev-dependencies] -black = "^22.6.0" -isort = "^5.10.1" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/m1/m1-cli/e2e/test_helpers.py b/m1/m1-cli/e2e/test_helpers.py deleted file mode 100644 index e3af62d43..000000000 --- a/m1/m1-cli/e2e/test_helpers.py +++ /dev/null @@ -1,174 +0,0 @@ -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -import logging -import os -import pathlib -import subprocess -import traceback -from dataclasses import dataclass - -from aptos_sdk.client import RestClient -from common import AccountInfo, build_image_name - -LOG = logging.getLogger(__name__) - -WORKING_DIR_IN_CONTAINER = "/tmp" - -# We pass this class into all test functions to help with calling the CLI, -# collecting output, and accessing common info. -@dataclass -class RunHelper: - host_working_directory: str - image_repo_with_project: str - image_tag: str - cli_path: str - test_count: int - - # This can be used by the tests to query the local testnet. - api_client: RestClient - - def __init__( - self, host_working_directory, image_repo_with_project, image_tag, cli_path - ): - if image_tag and cli_path: - raise RuntimeError("Cannot specify both image_tag and cli_path") - if not (image_tag or cli_path): - raise RuntimeError("Must specify one of image_tag and cli_path") - self.host_working_directory = host_working_directory - self.image_repo_with_project = image_repo_with_project - self.image_tag = image_tag - self.cli_path = os.path.abspath(cli_path) if cli_path else cli_path - self.test_count = 0 - self.api_client = RestClient(f"http://127.0.0.1:8080/v1") - - def build_image_name(self): - return build_image_name(self.image_repo_with_project, self.image_tag) - - # This function lets you pass call the CLI like you would normally, but really it is - # calling the CLI in a docker container and mounting the host working directory such - # that the container will write it results out to that directory. That way the CLI - # state / configuration is preserved between test cases. - def run_command(self, test_name, command, *args, **kwargs): - file_name = f"{self.test_count:03}_{test_name}" - self.test_count += 1 - - # Build command. - if self.image_tag: - full_command = [ - "docker", - "run", - # For why we have to set --user, see here: - # https://github.com/community/community/discussions/44243 - "--user", - f"{os.getuid()}:{os.getgid()}", - "--rm", - "--network", - "host", - "-i", - "-v", - f"{self.host_working_directory}:{WORKING_DIR_IN_CONTAINER}", - "--workdir", - WORKING_DIR_IN_CONTAINER, - self.build_image_name(), - ] + command - else: - full_command = [self.cli_path] + command[1:] - LOG.debug(f"Running command: {full_command}") - - # Create the output directory if necessary. - out_path = os.path.join(self.host_working_directory, "out") - pathlib.Path(out_path).mkdir(exist_ok=True) - - # Write the command we're going to run to file. - with open(os.path.join(out_path, f"{file_name}.command"), "w") as f: - f.write(" ".join(command)) - - # Run command. - try: - # If we're using a local CLI, set the working directory for subprocess.run. - if self.cli_path: - kwargs["cwd"] = self.host_working_directory - result = subprocess.run( - full_command, - *args, - check=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, - **kwargs, - ) - LOG.debug(f"Subcommand succeeded: {test_name}") - - write_subprocess_out(out_path, file_name, result) - - return result - except subprocess.CalledProcessError as e: - LOG.warn(f"Subcommand failed: {test_name}") - - # Write the exception to file. - with open(os.path.join(out_path, f"{file_name}.exception"), "w") as f: - f.write( - "".join( - traceback.format_exception( - etype=type(e), value=e, tb=e.__traceback__ - ) - ) - ) - - # Fortunately the result and exception of subprocess.run both have the - # stdout and stderr attributes on them. - write_subprocess_out(out_path, file_name, e) - - raise - - # If image_Tag is set, pull the test CLI image. We don't technically have to do - # this separately but it makes the steps clearer. Otherwise, cli_path must be - # set, in which case we ensure the file is there. - def prepare(self): - if self.image_tag: - image_name = self.build_image_name() - LOG.info(f"Pre-pulling image for CLI we're testing: {image_name}") - command = ["docker", "pull", image_name] - LOG.debug(f"Running command: {command}") - output = subprocess.check_output(command) - LOG.debug(f"Output: {output}") - else: - if not os.path.isfile(self.cli_path): - raise RuntimeError(f"CLI not found at path: {self.cli_path}") - - # Get the account info of the account created by test_init. - def get_account_info(self): - path = os.path.join(self.host_working_directory, ".aptos", "config.yaml") - with open(path) as f: - content = f.read().splitlines() - # To avoid using external deps we parse the file manually. - private_key = None - public_key = None - account_address = None - for line in content: - if "private_key: " in line: - private_key = line.split("private_key: ")[1].replace('"', "") - if "public_key: " in line: - public_key = line.split("public_key: ")[1].replace('"', "") - if "account: " in line: - account_address = line.split("account: ")[1].replace('"', "") - if not private_key or not public_key or not account_address: - raise RuntimeError(f"Failed to parse {path} to get account info") - return AccountInfo( - private_key=private_key, - public_key=public_key, - account_address=account_address, - ) - - -# This function helps with writing the stdout / stderr of a subprocess to files. -def write_subprocess_out(out_path, file_name, command_output): - LOG.debug(f"Stdout: {command_output.stdout}") - LOG.debug(f"Stderr: {command_output.stderr}") - - # Write stdout and stderr to file. - with open(os.path.join(out_path, f"{file_name}.stdout"), "w") as f: - f.write(command_output.stdout) - with open(os.path.join(out_path, f"{file_name}.stderr"), "w") as f: - f.write(command_output.stderr) diff --git a/m1/m1-cli/e2e/test_results.py b/m1/m1-cli/e2e/test_results.py deleted file mode 100644 index 661fe43be..000000000 --- a/m1/m1-cli/e2e/test_results.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -import logging -import typing -from dataclasses import dataclass, field -from functools import wraps - -LOG = logging.getLogger(__name__) - - -# This class holds info about passed / failed tests. -@dataclass(init=True) -class TestResults: - passed: typing.List[str] = field(default_factory=list) - failed: typing.List[typing.Tuple[str, Exception]] = field(default_factory=list) - - -# This is a decorator that you put above every test case. It handles capturing test -# success / failure so it can be reported at the end of the test suite. -def build_test_case_decorator(test_results: TestResults): - def test_case_inner(f): - @wraps(f) - def wrapper(*args, **kwds): - LOG.info(f"Running test: {f.__name__}") - try: - result = f(*args, test_name=f.__name__, **kwds) - test_results.passed.append(f.__name__) - return result - except Exception as e: - test_results.failed.append((f.__name__, e)) - return None - - return wrapper - - return test_case_inner - - -# We now define one TestResults that we'll use for every test case. This is a bit of a -# hack but it is the only way to then be able to provide a decorator that works out of -# the box. The alternative was to use a context manager and wrap every function call in -# it, but not only is that more verbose, but you'd have to provide the name of each test -# case manually to the context manager, whereas with this approach the name can be -# inferred from the function being decorated directly. -test_results = TestResults() - -# Then we define an instance of the decorator that uses that TestResults instance. -test_case = build_test_case_decorator(test_results) diff --git a/m1/m1-cli/homebrew/README.md b/m1/m1-cli/homebrew/README.md deleted file mode 100644 index e25d87d3b..000000000 --- a/m1/m1-cli/homebrew/README.md +++ /dev/null @@ -1,210 +0,0 @@ -# Homebrew Aptos - -Homebrew is a package manager that works for MacOS Silicon and Intel chips as well as Linux distributions like Debian and Ubuntu. - -The [Aptos command line interface (CLI)](https://aptos.dev/cli-tools/aptos-cli-tool/install-aptos-cli) may be installed via [Homebrew](https://brew.sh/) for simplicity. This is an in-depth overview of Homebrew and the Aptos formula. In this guide, we go over each section of the Homebrew formula and steps to implement changes in the future. - -## Quick guide - -- [Formula in Homebrew GitHub](https://github.com/Homebrew/homebrew-core/blob/master/Formula/aptos.rb) -- [Aptos 1.0.3 New Formula PR for GitHub](https://github.com/Homebrew/homebrew-core/pull/119832) -- [Aptos Formula Fix PR to use build_cli_release.sh](https://github.com/Homebrew/homebrew-core/pull/120051) - -## Getting started - -To begin, first ensure that homebrew is correctly installed on your computer. Visit [brew.sh](https://brew.sh/) to learn how you can set it up! - -To test that it works correctly, try - -```bash -brew help -``` - -Once homebrew is installed, run - -```bash -brew install aptos -``` - -to test that it installed correctly, try - -```bash -movement --help - -# This should return something like - -# movement 1.0.5 -# Movement Labs -# Command Line Interface (CLI) for developing and interacting with the Aptos blockchain -# ... -``` - -## Change guide - -Note: This guide is for developers who are trying to update the Aptos homebrew formula. - -Copy the `aptos.rb` file to your `homebrew` `formula` directory. For example, on macOS with an M1, this will likely be: - -```bash -/opt/homebrew/Library/Taps/homebrew/homebrew-core/Formula -``` - - -### Development - -After you've copied `aptos.rb` to your local `homebrew` `formula` directory, you can modify it and use the commands below for testing. - -```bash -# On Mac M1, homebrew formulas are located locally at -/opt/homebrew/Library/Taps/homebrew/homebrew-core/Formula - -# Before submitting changes run -brew audit --new-formula movement # For new formula -brew audit movement --strict --online -brew install movement -brew test movement - -# For debugging issues during the installation process you can do -brew install movement --interactive # Interactive, gives you access to the shell -brew install movement -d # Debug mode - -# Livecheck -brew livecheck --debug movement -``` - -### Committing changes - -Once you have audited and tested your brew formula using the commands above, make sure you: - -1. Commit your changes to `aptos-core` in `crates/aptos/homebrew`. -2. Fork the Homebrew Core repository per [How to Open a Homebrew Pull Request](https://docs.brew.sh/How-To-Open-a-Homebrew-Pull-Request#formulae-related-pull-request). -3. Create a PR on the [Homebrew Core](https://github.com/Homebrew/homebrew-core/pulls) repo with your changes. - -## Aptos.rb structure overview - -### Header - -```ruby -class Aptos < Formula - desc "Layer 1 blockchain built to support fair access to decentralized assets for all" - homepage "https://aptoslabs.com/" - url "https://github.com/aptos-labs/aptos-core/archive/refs/tags/aptos-cli-v1.0.3.tar.gz" - sha256 "670bb6cb841cb8a65294878af9a4f03d4cba2a598ab4550061fed3a4b1fe4e98" - license "Apache-2.0" - ... -``` - -### Bottles - -[Bottles](https://docs.brew.sh/Bottles#pour-bottle-pour_bottle) are precompiled binaries. This way people don't need to compile from source every time. - -> Bottles for homebrew/core formulae are created by [Brew Test Bot](https://docs.brew.sh/Brew-Test-Bot) when a pull request is submitted. If the formula builds successfully on each supported platform and a maintainer approves the change, [Brew Test Bot](https://docs.brew.sh/Brew-Test-Bot) updates its bottle do block and uploads each bottle to GitHub Packages. - -```ruby - ... - # IMPORTANT: These are automatically generated, you DO NOT need to add these manually, I'm adding them here as an example - bottle do - sha256 cellar: :any_skip_relocation, arm64_ventura: "40434b61e99cf9114a3715851d01c09edaa94b814f89864d57a18d00a8e0c4e9" - sha256 cellar: :any_skip_relocation, arm64_monterey: "edd6dcf9d627746a910d324422085eb4b06cdab654789a03b37133cd4868633c" - sha256 cellar: :any_skip_relocation, arm64_big_sur: "d9568107514168afc41e73bd3fd0fc45a6a9891a289857831f8ee027fb339676" - sha256 cellar: :any_skip_relocation, ventura: "d7289b5efca029aaa95328319ccf1d8a4813c7828f366314e569993eeeaf0003" - sha256 cellar: :any_skip_relocation, monterey: "ba58e1eb3398c725207ce9d6251d29b549cde32644c3d622cd286b86c7896576" - sha256 cellar: :any_skip_relocation, big_sur: "3e2431a6316b8f0ffa4db75758fcdd9dea162fdfb3dbff56f5e405bcbea4fedc" - sha256 cellar: :any_skip_relocation, x86_64_linux: "925113b4967ed9d3da78cd12745b1282198694a7f8c11d75b8c41451f8eff4b5" - end - ... -``` - -### Livecheck - -[Brew livecheck](https://docs.brew.sh/Brew-Livecheck) uses strategies to find the newest version of a formula or cask’s software by checking upstream. The strategy used below checks for all `aptos-cli-v` tags for `aptos-core`. The regex ensures that releases for other, non-CLI builds are not factored into livecheck. - -Livecheck is run on a schedule with BrewTestBot and will update the bottles automatically on a schedule to ensure they're up to date. For more info on how BrewTestBot and brew livecheck works, please see the [How does BrewTestBot work and when does it update formulae?](https://github.com/Homebrew/discussions/discussions/3083) discussion. - -```ruby -... - # This livecheck scans the releases folder and looks for all releases - # with matching regex of href="/tag/aptos-cli-v". This - # is done to automatically check for new release versions of the CLI. - livecheck do - url :stable - regex(/^aptos-cli[._-]v?(\d+(?:\.\d+)+)$/i) - end -... -``` - -To run livecheck for testing, we recommend including the `--debug` argument: - -```bash -brew livecheck --debug aptos -``` - -### Depends on and installation - -- `depends_on` is for specifying other [homebrew formulas as dependencies](https://docs.brew.sh/Formula-Cookbook#specifying-other-formulae-as-dependencies). -- Currently, we use v1.64 of Rust, as specified in the `Cargo.toml` file of the project. If we were to use the latest stable build of Rust -going forward, we would modify the formula slightly. See the comments below for more details. - - -```ruby - # Installs listed homebrew dependencies before Aptos installation - # Dependencies needed: https://aptos.dev/cli-tools/build-aptos-cli - # See scripts/dev_setup.sh in aptos-core for more info - depends_on "cmake" => :build - depends_on "rustup-init" => :build - uses_from_macos "llvm" => :build - - on_linux do - depends_on "pkg-config" => :build - depends_on "zip" => :build - depends_on "openssl@3" - depends_on "systemd" - end - - # Currently must compile with the same rustc version specified in the - # root Cargo.toml file of aptos-core (currently it is pegged to Rust - # v1.64). In the future if it becomes compatible with the latest Rust - # toolchain, we can remove the use of rustup-init, replacing it with a - # depends_on "rust" => :build - # above and build the binary without rustup as a dependency - # - # Uses build_cli_release.sh for creating the compiled binaries. - # This drastically reduces their size (ie. 2.2 GB on Linux for release - # build becomes 40 MB when run with opt-level = "z", strip, lto, etc). - # See cargo.toml [profile.cli] section for more details - def install - system "#{Formula["rustup-init"].bin}/rustup-init", - "-qy", "--no-modify-path", "--default-toolchain", "1.64" - ENV.prepend_path "PATH", HOMEBREW_CACHE/"cargo_cache/bin" - system "./scripts/cli/build_cli_release.sh", "homebrew" - bin.install "target/cli/aptos" - end -``` - -### Tests - -To conduct tests, run: - -```bash -brew test aptos -``` - -The current test generates a new key via the Movement CLI and ensures the shell output matches the filename(s) for that key. - -```ruby - ... - test do - assert_match(/output.pub/i, shell_output("#{bin}/aptos key generate --output-file output")) - end - ... -``` - -## Supporting resources - -- To view other Homebrew-related FAQs or ask questions yourself, visit the [discussions board](https://github.com/orgs/Homebrew/discussions). -- For similar Rust-related build examples, we recommend: - - [`rustfmt.rb`](https://github.com/Homebrew/homebrew-core/blob/master/Formula/rustfmt.rb) - - [`solana.rb`](https://github.com/Homebrew/homebrew-core/blob/master/Formula/solana.rb) -- Finally, note these key Homebew guides: - - [Homebrew Formula Cookbook](https://docs.brew.sh/Formula-Cookbook) - - [Creating and Running Your Own Homebrew Tap - Rust Runbook](https://publishing-project.rivendellweb.net/creating-and-running-your-own-homebrew-tap/) diff --git a/m1/m1-cli/homebrew/aptos.rb b/m1/m1-cli/homebrew/aptos.rb deleted file mode 100644 index 8d99b784d..000000000 --- a/m1/m1-cli/homebrew/aptos.rb +++ /dev/null @@ -1,35 +0,0 @@ -class Aptos < Formula - desc "Layer 1 blockchain built to support fair access to decentralized assets for all" - homepage "https://aptoslabs.com/" - url "https://github.com/aptos-labs/aptos-core/archive/refs/tags/aptos-cli-v1.0.3.tar.gz" - sha256 "670bb6cb841cb8a65294878af9a4f03d4cba2a598ab4550061fed3a4b1fe4e98" - license "Apache-2.0" - - livecheck do - url :stable - regex(/^aptos-cli[._-]v?(\d+(?:\.\d+)+)$/i) - end - - depends_on "cmake" => :build - depends_on "rustup-init" => :build - uses_from_macos "llvm" => :build - - on_linux do - depends_on "pkg-config" => :build - depends_on "zip" => :build - depends_on "openssl@3" - depends_on "systemd" - end - - def install - system "#{Formula["rustup-init"].bin}/rustup-init", - "-qy", "--no-modify-path", "--default-toolchain", "1.64" - ENV.prepend_path "PATH", HOMEBREW_CACHE/"cargo_cache/bin" - system "./scripts/cli/build_cli_release.sh", "homebrew" - bin.install "target/cli/aptos" - end - - test do - assert_match(/output.pub/i, shell_output("#{bin}/aptos key generate --output-file output")) - end -end \ No newline at end of file diff --git a/m1/m1-cli/src/account/create.rs b/m1/m1-cli/src/account/create.rs deleted file mode 100644 index 32070a403..000000000 --- a/m1/m1-cli/src/account/create.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::types::{CliCommand, CliTypedResult, TransactionOptions, TransactionSummary}; -use aptos_cached_packages::aptos_stdlib; -use aptos_types::account_address::AccountAddress; -use async_trait::async_trait; -use clap::Parser; - -// 1 APT -pub const DEFAULT_FUNDED_COINS: u64 = 100_000_000; - -/// Create a new account on-chain -/// -/// An account can be created by transferring coins, or by making an explicit -/// call to create an account. This will create an account with no coins, and -/// any coins will have to transferred afterwards. -#[derive(Debug, Parser)] -pub struct CreateAccount { - /// Address of the new account - #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] - pub(crate) account: AccountAddress, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for CreateAccount { - fn command_name(&self) -> &'static str { - "CreateAccount" - } - - async fn execute(self) -> CliTypedResult { - let address = self.account; - self.txn_options - .submit_transaction(aptos_stdlib::aptos_account_create_account(address)) - .await - .map(TransactionSummary::from) - } -} diff --git a/m1/m1-cli/src/account/create_resource_account.rs b/m1/m1-cli/src/account/create_resource_account.rs deleted file mode 100644 index 6e79071ab..000000000 --- a/m1/m1-cli/src/account/create_resource_account.rs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - account::derive_resource_account::ResourceAccountSeed, - common::types::{CliCommand, CliTypedResult, TransactionOptions, TransactionSummary}, -}; -use aptos_cached_packages::aptos_stdlib::resource_account_create_resource_account; -use aptos_rest_client::{ - aptos_api_types::{WriteResource, WriteSetChange}, - Transaction, -}; -use aptos_types::{account_address::AccountAddress, transaction::authenticator::AuthenticationKey}; -use async_trait::async_trait; -use clap::Parser; -use serde::Serialize; -use std::str::FromStr; - -/// Create a resource account on-chain -/// -/// This will create a resource account which can be used as an autonomous account -/// not controlled directly by one account. -#[derive(Debug, Parser)] -pub struct CreateResourceAccount { - /// Optional Resource Account authentication key. - #[clap(long, parse(try_from_str = AuthenticationKey::from_str))] - pub(crate) authentication_key: Option, - - #[clap(flatten)] - pub(crate) seed_args: ResourceAccountSeed, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -/// A shortened create resource account output -#[derive(Clone, Debug, Serialize)] -pub struct CreateResourceAccountSummary { - pub resource_account: Option, - #[serde(flatten)] - pub transaction_summary: TransactionSummary, -} - -impl From for CreateResourceAccountSummary { - fn from(transaction: Transaction) -> Self { - let transaction_summary = TransactionSummary::from(&transaction); - - let mut summary = CreateResourceAccountSummary { - transaction_summary, - resource_account: None, - }; - - if let Transaction::UserTransaction(txn) = transaction { - summary.resource_account = txn.info.changes.iter().find_map(|change| match change { - WriteSetChange::WriteResource(WriteResource { address, data, .. }) => { - if data.typ.name.as_str() == "Account" - && *address.inner().to_hex() != *txn.request.sender.inner().to_hex() - { - Some(*address.inner()) - } else { - None - } - }, - _ => None, - }); - } - - summary - } -} - -#[async_trait] -impl CliCommand for CreateResourceAccount { - fn command_name(&self) -> &'static str { - "CreateResourceAccount" - } - - async fn execute(self) -> CliTypedResult { - let authentication_key: Vec = if let Some(key) = self.authentication_key { - bcs::to_bytes(&key)? - } else { - vec![] - }; - self.txn_options - .submit_transaction(resource_account_create_resource_account( - self.seed_args.seed()?, - authentication_key, - )) - .await - .map(CreateResourceAccountSummary::from) - } -} diff --git a/m1/m1-cli/src/account/derive_resource_account.rs b/m1/m1-cli/src/account/derive_resource_account.rs deleted file mode 100644 index 2b14449df..000000000 --- a/m1/m1-cli/src/account/derive_resource_account.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::types::{CliCommand, CliError, CliTypedResult}; -use aptos_sdk::rest_client::aptos_api_types::HexEncodedBytes; -use aptos_types::account_address::{create_resource_address, AccountAddress}; -use async_trait::async_trait; -use clap::Parser; -use std::{fmt::Formatter, str::FromStr}; - -/// Encoding for the Resource account seed -#[derive(Debug, Clone, Copy)] -pub enum SeedEncoding { - Bcs, - Hex, - Utf8, -} - -const BCS: &str = "bcs"; -const UTF_8: &str = "utf8"; -const HEX: &str = "hex"; - -impl Default for SeedEncoding { - fn default() -> Self { - SeedEncoding::Bcs - } -} - -impl std::fmt::Display for SeedEncoding { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - SeedEncoding::Bcs => BCS, - SeedEncoding::Hex => HEX, - SeedEncoding::Utf8 => UTF_8, - }) - } -} - -impl FromStr for SeedEncoding { - type Err = CliError; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - BCS => Ok(Self::Bcs), - HEX => Ok(Self::Hex), - UTF_8 | "utf-8" | "utf_8" => Ok(Self::Utf8), - _ => Err(CliError::UnableToParse( - "seed-encoding", - "For --seed-encoding please provide one of ['bcs','hex', 'utf8']".to_string(), - )), - } - } -} - -/// A generic interface for allowing for different types of seed phrase inputs -/// -/// The easiest to use is `string_seed` as it will match directly with the b"string" notation in Move. -#[derive(Debug, Parser)] -pub struct ResourceAccountSeed { - /// Resource account seed - /// - /// Seed used in generation of the AccountId of the resource account - /// The seed will be converted to bytes using the encoding from `--seed-encoding`, defaults to `BCS` - #[clap(long)] - pub(crate) seed: String, - - /// Resource account seed encoding - /// - /// The encoding can be one of `Bcs`, `Utf8`, and `Hex`. - /// - /// - Bcs is the legacy functionality of the CLI, it will BCS encode the string, but can be confusing for users e.g. `"ab" -> vector[0x2, 0x61, 0x62]` - /// - Utf8 will encode the string as raw UTF-8 bytes, similar to in Move `b"string"` e.g. `"ab" -> vector[0x61, 0x62]` - /// - Hex will encode the string as raw hex encoded bytes e.g. `"0x6162" -> vector[0x61, 0x62]` - #[clap(long, default_value_t = SeedEncoding::Bcs)] - pub(crate) seed_encoding: SeedEncoding, -} - -impl ResourceAccountSeed { - pub fn seed(self) -> CliTypedResult> { - match self.seed_encoding { - SeedEncoding::Bcs => Ok(bcs::to_bytes(self.seed.as_str())?), - SeedEncoding::Utf8 => Ok(self.seed.as_bytes().to_vec()), - SeedEncoding::Hex => HexEncodedBytes::from_str(self.seed.as_str()) - .map(|inner| inner.0) - .map_err(|err| CliError::UnableToParse("seed", err.to_string())), - } - } -} - -/// Derive the address for a resource account -/// -/// This will not create a resource account, but instead give the deterministic address given -/// a source address and seed. -#[derive(Debug, Parser)] -pub struct DeriveResourceAccount { - /// Address of the creator's account - #[clap(long, alias = "account", parse(try_from_str=crate::common::types::load_account_arg))] - pub(crate) address: AccountAddress, - - #[clap(flatten)] - pub(crate) seed_args: ResourceAccountSeed, -} - -#[async_trait] -impl CliCommand for DeriveResourceAccount { - fn command_name(&self) -> &'static str { - "DeriveResourceAccountAddress" - } - - async fn execute(self) -> CliTypedResult { - let seed = self.seed_args.seed()?; - Ok(create_resource_address(self.address, &seed)) - } -} diff --git a/m1/m1-cli/src/account/fund.rs b/m1/m1-cli/src/account/fund.rs deleted file mode 100644 index c3846aad6..000000000 --- a/m1/m1-cli/src/account/fund.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - account::create::DEFAULT_FUNDED_COINS, - common::{ - types::{CliCommand, CliTypedResult, FaucetOptions, ProfileOptions, RestOptions}, - utils::{fund_account, wait_for_transactions, fund_pub_key}, - }, -}; -use aptos_types::account_address::AccountAddress; -use async_trait::async_trait; -use clap::Parser; - -/// Fund an account with tokens from a faucet -/// -/// This will create an account if it doesn't exist with the faucet. This is mostly useful -/// for local development and devnet. -#[derive(Debug, Parser)] -pub struct FundWithFaucet { - /// Address to fund - /// - /// If the account wasn't previously created, it will be created when being funded - #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] - pub(crate) account: AccountAddress, - - /// Number of Octas to fund the account from the faucet - /// - /// The amount added to the account may be limited by the faucet, and may be less - /// than the amount requested. - #[clap(long, default_value_t = DEFAULT_FUNDED_COINS)] - pub(crate) amount: u64, - - #[clap(flatten)] - pub(crate) faucet_options: FaucetOptions, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, -} - -#[async_trait] -impl CliCommand for FundWithFaucet { - fn command_name(&self) -> &'static str { - "FundWithFaucet" - } - - async fn execute(self) -> CliTypedResult { - // todo: the below is a hotfix. Determine why the auth_key parameter in the rpc is not working - /* - let hashes = fund_account( - self.faucet_options.faucet_url(&self.profile_options)?, - self.amount, - self.account, - ) - .await?; - */ - let hashes = fund_pub_key( - self.faucet_options.faucet_url(&self.profile_options)?, - (self.profile_options.public_key()?).to_string() - ).await?; - let client = self.rest_options.client(&self.profile_options)?; - wait_for_transactions(&client, hashes).await?; - return Ok(format!( - "Added 10 MOV to account {}", - self.account - )); - } -} diff --git a/m1/m1-cli/src/account/key_rotation.rs b/m1/m1-cli/src/account/key_rotation.rs deleted file mode 100644 index da008fb82..000000000 --- a/m1/m1-cli/src/account/key_rotation.rs +++ /dev/null @@ -1,338 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::{ - types::{ - account_address_from_public_key, CliCommand, CliConfig, CliError, CliTypedResult, - ConfigSearchMode, EncodingOptions, EncodingType, ExtractPublicKey, ParsePrivateKey, - ProfileConfig, ProfileOptions, PublicKeyInputOptions, RestOptions, RotationProofChallenge, - TransactionOptions, TransactionSummary, - }, - utils::{prompt_yes, prompt_yes_with_override, read_line}, -}; -use aptos_cached_packages::aptos_stdlib; -use aptos_crypto::{ - ed25519::{Ed25519PrivateKey, Ed25519PublicKey}, - PrivateKey, SigningKey, -}; -use aptos_rest_client::{ - aptos_api_types::{AptosError, AptosErrorCode}, - error::{AptosErrorResponse, RestError}, - Client, -}; -use aptos_types::{account_address::AccountAddress, account_config::CORE_CODE_ADDRESS}; -use async_trait::async_trait; -use clap::Parser; -use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, path::PathBuf}; - -/// Rotate an account's authentication key -/// -/// Rotating the account's authentication key allows you to use a new -/// private key. You must provide a new private key. Once it is -/// rotated you will need to use the original account address, with the -/// new private key. There is an interactive prompt to help you add it -/// to a new profile. -#[derive(Debug, Parser)] -pub struct RotateKey { - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - - /// File name that contains the new private key encoded in the type from `--encoding` - #[clap(long, group = "new_private_key", parse(from_os_str))] - pub(crate) new_private_key_file: Option, - - /// New private key encoded in the type from `--encoding` - #[clap(long, group = "new_private_key")] - pub(crate) new_private_key: Option, - - /// Name of the profile to save the new private key - /// - /// If not provided, it will interactively have you save a profile, - /// unless `--skip_saving_profile` is provided - #[clap(long)] - pub(crate) save_to_profile: Option, - - /// Skip saving profile - /// - /// This skips the interactive profile saving after rotating the authentication key - #[clap(long)] - pub(crate) skip_saving_profile: bool, -} - -impl ParsePrivateKey for RotateKey {} - -impl RotateKey { - /// Extract private key from CLI args - pub fn extract_private_key( - &self, - encoding: EncodingType, - ) -> CliTypedResult> { - self.parse_private_key( - encoding, - self.new_private_key_file.clone(), - self.new_private_key.clone(), - ) - } -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct RotateSummary { - message: Option, - transaction: TransactionSummary, -} - -#[async_trait] -impl CliCommand for RotateKey { - fn command_name(&self) -> &'static str { - "RotateKey" - } - - async fn execute(self) -> CliTypedResult { - let new_private_key = self - .extract_private_key(self.txn_options.encoding_options.encoding)? - .ok_or_else(|| { - CliError::CommandArgumentError( - "One of ['--new-private-key', '--new-private-key-file'] must be used" - .to_string(), - ) - })?; - - let (current_private_key, sender_address) = self.txn_options.get_key_and_address()?; - - // Get sequence number for account - let sequence_number = self.txn_options.sequence_number(sender_address).await?; - let auth_key = self.txn_options.auth_key(sender_address).await?; - - let rotation_proof = RotationProofChallenge { - account_address: CORE_CODE_ADDRESS, - module_name: "account".to_string(), - struct_name: "RotationProofChallenge".to_string(), - sequence_number, - originator: sender_address, - current_auth_key: AccountAddress::from_bytes(auth_key) - .map_err(|err| CliError::UnableToParse("auth_key", err.to_string()))?, - new_public_key: new_private_key.public_key().to_bytes().to_vec(), - }; - - let rotation_msg = - bcs::to_bytes(&rotation_proof).map_err(|err| CliError::BCS("rotation_proof", err))?; - - // Signs the struct using both the current private key and the next private key - let rotation_proof_signed_by_current_private_key = - current_private_key.sign_arbitrary_message(&rotation_msg.clone()); - let rotation_proof_signed_by_new_private_key = - new_private_key.sign_arbitrary_message(&rotation_msg); - - let txn_summary = self - .txn_options - .submit_transaction(aptos_stdlib::account_rotate_authentication_key( - 0, - // Existing public key - current_private_key.public_key().to_bytes().to_vec(), - 0, - // New public key - new_private_key.public_key().to_bytes().to_vec(), - rotation_proof_signed_by_current_private_key - .to_bytes() - .to_vec(), - rotation_proof_signed_by_new_private_key.to_bytes().to_vec(), - )) - .await - .map(TransactionSummary::from)?; - - let string = serde_json::to_string_pretty(&txn_summary) - .map_err(|err| CliError::UnableToParse("transaction summary", err.to_string()))?; - - eprintln!("{}", string); - - if let Some(txn_success) = txn_summary.success { - if !txn_success { - return Err(CliError::ApiError( - "Transaction was not executed successfully".to_string(), - )); - } - } else { - return Err(CliError::UnexpectedError( - "Malformed transaction response".to_string(), - )); - } - - let mut profile_name: String; - - if self.save_to_profile.is_none() { - if self.skip_saving_profile - || !prompt_yes("Do you want to create a profile for the new key?") - { - return Ok(RotateSummary { - transaction: txn_summary, - message: None, - }); - } - - eprintln!("Enter the name for the profile"); - profile_name = read_line("Profile name")?.trim().to_string(); - } else { - // We can safely unwrap here - profile_name = self.save_to_profile.unwrap(); - } - - // Check if profile name exists - let mut config = CliConfig::load(ConfigSearchMode::CurrentDirAndParents)?; - - if let Some(ref profiles) = config.profiles { - if profiles.contains_key(&profile_name) { - if let Err(cli_err) = prompt_yes_with_override( - format!( - "Profile {} exits. Do you want to provide a new profile name?", - profile_name - ) - .as_str(), - self.txn_options.prompt_options, - ) { - match cli_err { - CliError::AbortedError => { - return Ok(RotateSummary { - transaction: txn_summary, - message: None, - }); - } - _ => { - return Err(cli_err); - } - } - } - - eprintln!("Enter the name for the profile"); - profile_name = read_line("Profile name")?.trim().to_string(); - } - } - - if profile_name.is_empty() { - return Err(CliError::AbortedError); - } - - let mut profile_config = ProfileConfig { - private_key: Some(new_private_key.clone()), - public_key: Some(new_private_key.public_key()), - account: Some(sender_address), - ..self.txn_options.profile_options.profile()? - }; - - if let Some(url) = self.txn_options.rest_options.url { - profile_config.rest_url = Some(url.into()); - } - - if config.profiles.is_none() { - config.profiles = Some(BTreeMap::new()); - } - - config - .profiles - .as_mut() - .unwrap() - .insert(profile_name.clone(), profile_config); - config.save()?; - - eprintln!("Profile {} is saved.", profile_name); - - Ok(RotateSummary { - transaction: txn_summary, - message: Some(format!("Profile {} is saved.", profile_name)), - }) - } -} - -/// Lookup the account address through the on-chain lookup table -/// -/// If the account is rotated, it will provide the address accordingly. If the account was not -/// rotated, it will provide the derived address only if the account exists onchain. -#[derive(Debug, Parser)] -pub struct LookupAddress { - #[clap(flatten)] - pub(crate) encoding_options: EncodingOptions, - - #[clap(flatten)] - pub(crate) public_key_options: PublicKeyInputOptions, - - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, - - #[clap(flatten)] - pub(crate) rest_options: RestOptions, -} - -impl LookupAddress { - pub(crate) fn public_key(&self) -> CliTypedResult { - self.public_key_options - .extract_public_key(self.encoding_options.encoding, &self.profile_options) - } - - /// Builds a rest client - fn rest_client(&self) -> CliTypedResult { - self.rest_options.client(&self.profile_options) - } -} - -#[async_trait] -impl CliCommand for LookupAddress { - fn command_name(&self) -> &'static str { - "LookupAddress" - } - - async fn execute(self) -> CliTypedResult { - let rest_client = self.rest_client()?; - - // TODO: Support arbitrary auth key to support other types like multie25519 - let address = account_address_from_public_key(&self.public_key()?); - Ok(lookup_address(&rest_client, address, true).await?) - } -} - -pub async fn lookup_address( - rest_client: &Client, - address_key: AccountAddress, - must_exist: bool, -) -> Result { - let originating_resource: OriginatingResource = rest_client - .get_account_resource_bcs(CORE_CODE_ADDRESS, "0x1::account::OriginatingAddress") - .await? - .into_inner(); - - let table_handle = originating_resource.address_map.handle; - - // The derived address that can be used to look up the original address - match rest_client - .get_table_item_bcs( - table_handle, - "address", - "address", - address_key.to_hex_literal(), - ) - .await - { - Ok(inner) => Ok(inner.into_inner()), - Err(RestError::Api(..)) => { - // If the table item wasn't found, we may check if the account exists - if !must_exist { - Ok(address_key) - } else { - rest_client - .get_account_bcs(address_key) - .await - .map(|_| address_key) - } - } - Err(err) => Err(err), - } -} - -#[derive(Deserialize)] -pub struct OriginatingResource { - pub address_map: Table, -} - -#[derive(Deserialize)] -pub struct Table { - pub handle: AccountAddress, -} diff --git a/m1/m1-cli/src/account/list.rs b/m1/m1-cli/src/account/list.rs deleted file mode 100644 index c406b2963..000000000 --- a/m1/m1-cli/src/account/list.rs +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::types::{ - CliCommand, CliConfig, CliError, CliTypedResult, ConfigSearchMode, ProfileOptions, RestOptions, -}; -use aptos_types::account_address::AccountAddress; -use async_trait::async_trait; -use clap::{ArgEnum, Parser}; -use serde_json::json; -use std::{ - fmt::{Display, Formatter}, - str::FromStr, -}; - -#[derive(ArgEnum, Clone, Copy, Debug)] -pub enum ListQuery { - Balance, - Modules, - Resources, -} - -impl Display for ListQuery { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let str = match self { - ListQuery::Balance => "balance", - ListQuery::Modules => "modules", - ListQuery::Resources => "resources", - }; - write!(f, "{}", str) - } -} - -impl FromStr for ListQuery { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "balance" => Ok(ListQuery::Balance), - "modules" => Ok(ListQuery::Modules), - "resources" => Ok(ListQuery::Resources), - _ => Err("Invalid query. Valid values are balance, modules, resources"), - } - } -} - -/// List resources, modules, or balance owned by an address -/// -/// This allows you to list the current resources at the time of query. This can change due to -/// any transactions that have occurred after the request. -#[derive(Debug, Parser)] -pub struct ListAccount { - /// Address of the account you want to list resources/modules/balance for - #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] - pub(crate) account: Option, - - /// Type of items to list: [balance, resources, modules] - #[clap(long, default_value_t = ListQuery::Resources)] - pub(crate) query: ListQuery, - - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, -} - -#[async_trait] -impl CliCommand> for ListAccount { - fn command_name(&self) -> &'static str { - "ListAccount" - } - - async fn execute(self) -> CliTypedResult> { - let account = if let Some(account) = self.account { - account - } else if let Some(Some(account)) = CliConfig::load_profile( - self.profile_options.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|p| p.account) - { - account - } else { - return Err(CliError::CommandArgumentError( - "Please provide an account using --account or run movement init".to_string(), - )); - }; - - let client = self.rest_options.client(&self.profile_options)?; - let response = match self.query { - ListQuery::Balance => vec![ - client - .get_account_resource( - account, - "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>", - ) - .await? - .into_inner() - .unwrap() - .data, - ], - ListQuery::Modules => client - .get_account_modules(account) - .await? - .into_inner() - .into_iter() - .map(|module| json!(module.try_parse_abi().unwrap())) - .collect::>(), - ListQuery::Resources => client - .get_account_resources(account) - .await? - .into_inner() - .into_iter() - .map(|resource| { - let mut map = serde_json::Map::new(); - map.insert(resource.resource_type.to_string(), resource.data); - serde_json::Value::Object(map) - }) - .collect::>(), - }; - - Ok(response) - } -} diff --git a/m1/m1-cli/src/account/mod.rs b/m1/m1-cli/src/account/mod.rs deleted file mode 100644 index 988f065a3..000000000 --- a/m1/m1-cli/src/account/mod.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::types::{CliCommand, CliResult}; -use clap::Subcommand; - -pub mod create; -pub mod create_resource_account; -pub mod derive_resource_account; -pub mod fund; -pub mod key_rotation; -pub mod list; -pub mod multisig_account; -pub mod transfer; - -/// Tool for interacting with accounts -/// -/// This tool is used to create accounts, get information about the -/// account's resources, and transfer resources between accounts. -#[derive(Debug, Subcommand)] -pub enum AccountTool { - Create(create::CreateAccount), - CreateResourceAccount(create_resource_account::CreateResourceAccount), - DeriveResourceAccountAddress(derive_resource_account::DeriveResourceAccount), - FundWithFaucet(fund::FundWithFaucet), - List(list::ListAccount), - LookupAddress(key_rotation::LookupAddress), - RotateKey(key_rotation::RotateKey), - Transfer(transfer::TransferCoins), -} - -impl AccountTool { - pub async fn execute(self) -> CliResult { - match self { - AccountTool::Create(tool) => tool.execute_serialized().await, - AccountTool::CreateResourceAccount(tool) => tool.execute_serialized().await, - AccountTool::DeriveResourceAccountAddress(tool) => tool.execute_serialized().await, - AccountTool::FundWithFaucet(tool) => tool.execute_serialized().await, - AccountTool::List(tool) => tool.execute_serialized().await, - AccountTool::LookupAddress(tool) => tool.execute_serialized().await, - AccountTool::RotateKey(tool) => tool.execute_serialized().await, - AccountTool::Transfer(tool) => tool.execute_serialized().await, - } - } -} - -/// Tool for interacting with multisig accounts -#[derive(Debug, Subcommand)] -pub enum MultisigAccountTool { - Approve(multisig_account::Approve), - Create(multisig_account::Create), - CreateTransaction(multisig_account::CreateTransaction), - Execute(multisig_account::Execute), - ExecuteReject(multisig_account::ExecuteReject), - Reject(multisig_account::Reject), -} - -impl MultisigAccountTool { - pub async fn execute(self) -> CliResult { - match self { - MultisigAccountTool::Approve(tool) => tool.execute_serialized().await, - MultisigAccountTool::Create(tool) => tool.execute_serialized().await, - MultisigAccountTool::CreateTransaction(tool) => tool.execute_serialized().await, - MultisigAccountTool::Execute(tool) => tool.execute_serialized().await, - MultisigAccountTool::ExecuteReject(tool) => tool.execute_serialized().await, - MultisigAccountTool::Reject(tool) => tool.execute_serialized().await, - } - } -} diff --git a/m1/m1-cli/src/account/multisig_account.rs b/m1/m1-cli/src/account/multisig_account.rs deleted file mode 100644 index 0d60380af..000000000 --- a/m1/m1-cli/src/account/multisig_account.rs +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::types::{ - CliCommand, CliTypedResult, EntryFunctionArguments, MultisigAccount, TransactionOptions, - TransactionSummary, -}; -use aptos_cached_packages::aptos_stdlib; -use aptos_rest_client::{ - aptos_api_types::{WriteResource, WriteSetChange}, - Transaction, -}; -use aptos_types::{ - account_address::AccountAddress, - transaction::{Multisig, MultisigTransactionPayload, TransactionPayload}, -}; -use async_trait::async_trait; -use bcs::to_bytes; -use clap::Parser; -use serde::Serialize; - -/// Create a new multisig account (v2) on-chain. -/// -/// This will create a new multisig account and make the sender one of the owners. -#[derive(Debug, Parser)] -pub struct Create { - /// Addresses of additional owners for the new multisig, beside the transaction sender. - #[clap(long, multiple_values = true, parse(try_from_str=crate::common::types::load_account_arg))] - pub(crate) additional_owners: Vec, - /// The number of signatures (approvals or rejections) required to execute or remove a proposed - /// transaction. - #[clap(long)] - pub(crate) num_signatures_required: u64, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -/// A shortened create multisig account output -#[derive(Clone, Debug, Serialize)] -pub struct CreateSummary { - #[serde(flatten)] - pub multisig_account: Option, - #[serde(flatten)] - pub transaction_summary: TransactionSummary, -} - -impl From for CreateSummary { - fn from(transaction: Transaction) -> Self { - let transaction_summary = TransactionSummary::from(&transaction); - - let mut summary = CreateSummary { - transaction_summary, - multisig_account: None, - }; - - if let Transaction::UserTransaction(txn) = transaction { - summary.multisig_account = txn.info.changes.iter().find_map(|change| match change { - WriteSetChange::WriteResource(WriteResource { address, data, .. }) => { - if data.typ.name.as_str() == "Account" - && *address.inner().to_hex() != *txn.request.sender.inner().to_hex() - { - Some(MultisigAccount { - multisig_address: *address.inner(), - }) - } else { - None - } - }, - _ => None, - }); - } - - summary - } -} - -#[async_trait] -impl CliCommand for Create { - fn command_name(&self) -> &'static str { - "CreateMultisig" - } - - async fn execute(self) -> CliTypedResult { - self.txn_options - .submit_transaction(aptos_stdlib::multisig_account_create_with_owners( - self.additional_owners, - self.num_signatures_required, - // TODO: Support passing in custom metadata. - vec![], - vec![], - )) - .await - .map(CreateSummary::from) - } -} - -/// Propose a new multisig transaction. -/// -/// As one of the owners of the multisig, propose a new transaction. This also implicitly approves -/// the created transaction so it has one approval initially. In order for the transaction to be -/// executed, it needs as many approvals as the number of signatures required. -#[derive(Debug, Parser)] -pub struct CreateTransaction { - #[clap(flatten)] - pub(crate) multisig_account: MultisigAccount, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) entry_function_args: EntryFunctionArguments, -} - -#[async_trait] -impl CliCommand for CreateTransaction { - fn command_name(&self) -> &'static str { - "CreateTransactionMultisig" - } - - async fn execute(self) -> CliTypedResult { - let payload = MultisigTransactionPayload::EntryFunction( - self.entry_function_args.create_entry_function_payload()?, - ); - self.txn_options - .submit_transaction(aptos_stdlib::multisig_account_create_transaction( - self.multisig_account.multisig_address, - to_bytes(&payload)?, - )) - .await - .map(|inner| inner.into()) - } -} - -/// Approve a multisig transaction. -/// -/// As one of the owners of the multisig, approve a transaction proposed for the multisig. -/// With enough approvals (as many as the number of signatures required), the transaction can be -/// executed (See Execute). -#[derive(Debug, Parser)] -pub struct Approve { - #[clap(flatten)] - pub(crate) multisig_account: MultisigAccount, - /// The sequence number of the multisig transaction to approve. The sequence number increments - /// for every new multisig transaction. - #[clap(long)] - pub(crate) sequence_number: u64, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for Approve { - fn command_name(&self) -> &'static str { - "ApproveMultisig" - } - - async fn execute(self) -> CliTypedResult { - self.txn_options - .submit_transaction(aptos_stdlib::multisig_account_approve_transaction( - self.multisig_account.multisig_address, - self.sequence_number, - )) - .await - .map(|inner| inner.into()) - } -} - -/// Reject a multisig transaction. -/// -/// As one of the owners of the multisig, reject a transaction proposed for the multisig. -/// With enough rejections (as many as the number of signatures required), the transaction can be -/// completely removed (See ExecuteReject). -#[derive(Debug, Parser)] -pub struct Reject { - #[clap(flatten)] - pub(crate) multisig_account: MultisigAccount, - /// The sequence number of the multisig transaction to reject. The sequence number increments - /// for every new multisig transaction. - #[clap(long)] - pub(crate) sequence_number: u64, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for Reject { - fn command_name(&self) -> &'static str { - "RejectMultisig" - } - - async fn execute(self) -> CliTypedResult { - self.txn_options - .submit_transaction(aptos_stdlib::multisig_account_reject_transaction( - self.multisig_account.multisig_address, - self.sequence_number, - )) - .await - .map(|inner| inner.into()) - } -} - -/// Execute a proposed multisig transaction. -/// -/// The transaction to be executed needs to have as many approvals as the number of signatures -/// required. -#[derive(Debug, Parser)] -pub struct Execute { - #[clap(flatten)] - pub(crate) multisig_account: MultisigAccount, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for Execute { - fn command_name(&self) -> &'static str { - "ExecuteMultisig" - } - - async fn execute(self) -> CliTypedResult { - let payload = TransactionPayload::Multisig(Multisig { - multisig_address: self.multisig_account.multisig_address, - // TODO: Support passing an explicit payload - transaction_payload: None, - }); - self.txn_options - .submit_transaction(payload) - .await - .map(|inner| inner.into()) - } -} - -/// Remove a proposed multisig transaction. -/// -/// The transaction to be removed needs to have as many rejections as the number of signatures -/// required. -#[derive(Debug, Parser)] -pub struct ExecuteReject { - #[clap(flatten)] - pub(crate) multisig_account: MultisigAccount, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for ExecuteReject { - fn command_name(&self) -> &'static str { - "ExecuteRejectMultisig" - } - - async fn execute(self) -> CliTypedResult { - self.txn_options - .submit_transaction(aptos_stdlib::multisig_account_execute_rejected_transaction( - self.multisig_account.multisig_address, - )) - .await - .map(|inner| inner.into()) - } -} diff --git a/m1/m1-cli/src/account/transfer.rs b/m1/m1-cli/src/account/transfer.rs deleted file mode 100644 index ce8b697dd..000000000 --- a/m1/m1-cli/src/account/transfer.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::types::{CliCommand, CliTypedResult, TransactionOptions}; -use aptos_cached_packages::aptos_stdlib; -use aptos_rest_client::{ - aptos_api_types::{HashValue, WriteResource, WriteSetChange}, - Transaction, -}; -use aptos_types::account_address::AccountAddress; -use async_trait::async_trait; -use clap::Parser; -use serde::Serialize; -use std::collections::BTreeMap; - -// TODO: Add ability to transfer non-APT coins -// TODO: Add ability to not create account by default -/// Transfer APT between accounts -/// -#[derive(Debug, Parser)] -pub struct TransferCoins { - /// Address of account to send APT to - #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] - pub(crate) account: AccountAddress, - - /// Amount of Octas (10^-8 APT) to transfer - #[clap(long)] - pub(crate) amount: u64, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for TransferCoins { - fn command_name(&self) -> &'static str { - "TransferCoins" - } - - async fn execute(self) -> CliTypedResult { - self.txn_options - .submit_transaction(aptos_stdlib::aptos_account_transfer( - self.account, - self.amount, - )) - .await - .map(TransferSummary::from) - } -} - -const SUPPORTED_COINS: [&str; 1] = ["0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"]; - -/// A shortened transaction output -#[derive(Clone, Debug, Serialize)] -pub struct TransferSummary { - pub gas_unit_price: u64, - pub gas_used: u64, - pub balance_changes: BTreeMap, - pub sender: AccountAddress, - pub success: bool, - pub version: u64, - pub vm_status: String, - pub transaction_hash: HashValue, -} - -impl TransferSummary { - pub fn octa_spent(&self) -> u64 { - self.gas_unit_price * self.gas_used - } -} - -impl From for TransferSummary { - fn from(transaction: Transaction) -> Self { - if let Transaction::UserTransaction(txn) = transaction { - let vm_status = txn.info.vm_status; - let success = txn.info.success; - let sender = *txn.request.sender.inner(); - let gas_unit_price = txn.request.gas_unit_price.0; - let gas_used = txn.info.gas_used.0; - let transaction_hash = txn.info.hash; - let version = txn.info.version.0; - let balance_changes = txn - .info - .changes - .into_iter() - .filter_map(|change| match change { - WriteSetChange::WriteResource(WriteResource { address, data, .. }) => { - if SUPPORTED_COINS.contains(&data.typ.to_string().as_str()) { - Some(( - *address.inner(), - serde_json::to_value(data.data).unwrap_or_default(), - )) - } else { - None - } - }, - _ => None, - }) - .collect(); - - TransferSummary { - gas_unit_price, - gas_used, - balance_changes, - sender, - success, - version, - vm_status, - transaction_hash, - } - } else { - panic!("Can't call From for a non UserTransaction") - } - } -} diff --git a/m1/m1-cli/src/common/init.rs b/m1/m1-cli/src/common/init.rs deleted file mode 100644 index 9cd60c4b7..000000000 --- a/m1/m1-cli/src/common/init.rs +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - account::key_rotation::lookup_address, - common::{ - types::{ - account_address_from_public_key, CliCommand, CliConfig, CliError, CliTypedResult, - ConfigSearchMode, EncodingOptions, PrivateKeyInputOptions, ProfileConfig, - ProfileOptions, PromptOptions, RngArgs, DEFAULT_PROFILE, - }, - utils::{fund_account, fund_pub_key, prompt_yes_with_override, read_line, wait_for_transactions}, - }, -}; -use aptos_crypto::{ed25519::Ed25519PrivateKey, PrivateKey, ValidCryptoMaterialStringExt}; -use aptos_rest_client::{ - aptos_api_types::{AptosError, AptosErrorCode}, - error::{AptosErrorResponse, RestError}, -}; -use async_trait::async_trait; -use clap::Parser; -use reqwest::Url; -use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, str::FromStr}; - -// -const SEED_NODE_1_REST : &str = "https://seed-node1.movementlabs.xyz"; - -/// 1 APT (might not actually get that much, depending on the faucet) -const NUM_DEFAULT_OCTAS: u64 = 100000000; - -/// Tool to initialize current directory for the aptos tool -/// -/// Configuration will be pushed into .aptos/config.yaml -#[derive(Debug, Parser)] -pub struct InitTool { - /// Network to use for default settings - /// - /// If custom `rest_url` and `faucet_url` are wanted, use `custom` - #[clap(long)] - pub network: Option, - - /// URL to a fullnode on the network - #[clap(long)] - pub rest_url: Option, - - /// URL for the Faucet endpoint - #[clap(long)] - pub faucet_url: Option, - - /// Whether to skip the faucet for a non-faucet endpoint - #[clap(long)] - pub skip_faucet: bool, - - #[clap(flatten)] - pub rng_args: RngArgs, - #[clap(flatten)] - pub(crate) private_key_options: PrivateKeyInputOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, - #[clap(flatten)] - pub(crate) encoding_options: EncodingOptions, -} - -#[async_trait] -impl CliCommand<()> for InitTool { - fn command_name(&self) -> &'static str { - "MovementInit" - } - - async fn execute(self) -> CliTypedResult<()> { - let mut config = if CliConfig::config_exists(ConfigSearchMode::CurrentDir) { - CliConfig::load(ConfigSearchMode::CurrentDir)? - } else { - CliConfig::default() - }; - - let profile_name = self - .profile_options - .profile_name() - .unwrap_or(DEFAULT_PROFILE); - - // Select profile we're using - let mut profile_config = if let Some(profile_config) = config.remove_profile(profile_name) { - prompt_yes_with_override(&format!("Movement already initialized for profile {}, do you want to overwrite the existing config?", profile_name), self.prompt_options)?; - profile_config - } else { - ProfileConfig::default() - }; - - eprintln!("Configuring for profile {}", profile_name); - - // Choose a network - let network = if let Some(network) = self.network { - eprintln!("Configuring for network {:?}", network); - network - } else { - eprintln!( - "Choose network from [devnet, testnet, mainnet, local, custom | defaults to devnet]" - ); - let input = read_line("network")?; - let input = input.trim(); - if input.is_empty() { - eprintln!("No network given, using devnet..."); - Network::Devnet - } else { - Network::from_str(input)? - } - }; - - // Ensure that there is at least a REST URL set for the network - match network { - Network::Mainnet => { - profile_config.rest_url = - Some(SEED_NODE_1_REST.to_string()); - profile_config.faucet_url = - Some(SEED_NODE_1_REST.to_string()); - }, - Network::Testnet => { - profile_config.rest_url = - Some(SEED_NODE_1_REST.to_string()); - profile_config.faucet_url = - Some(SEED_NODE_1_REST.to_string()); - }, - Network::Devnet => { - profile_config.rest_url = Some(SEED_NODE_1_REST.to_string()); - profile_config.faucet_url = Some(SEED_NODE_1_REST.to_string()); - }, - Network::Local => { - profile_config.rest_url = Some("http://localhost:8080".to_string()); - profile_config.faucet_url = Some("http://localhost:8081".to_string()); - }, - Network::Custom => self.custom_network(&mut profile_config)?, - } - - // Private key - let private_key = if let Some(private_key) = self - .private_key_options - .extract_private_key_cli(self.encoding_options.encoding)? - { - eprintln!("Using command line argument for private key"); - private_key - } else { - eprintln!("Enter your private key as a hex literal (0x...) [Current: {} | No input: Generate new key (or keep one if present)]", profile_config.private_key.as_ref().map(|_| "Redacted").unwrap_or("None")); - let input = read_line("Private key")?; - let input = input.trim(); - if input.is_empty() { - if let Some(private_key) = profile_config.private_key { - eprintln!("No key given, keeping existing key..."); - private_key - } else { - eprintln!("No key given, generating key..."); - self.rng_args - .key_generator()? - .generate_ed25519_private_key() - } - } else { - Ed25519PrivateKey::from_encoded_string(input) - .map_err(|err| CliError::UnableToParse("Ed25519PrivateKey", err.to_string()))? - } - }; - let public_key = private_key.public_key(); - - let client = aptos_rest_client::Client::new( - Url::parse( - profile_config - .rest_url - .as_ref() - .expect("Must have rest client as created above"), - ) - .map_err(|err| CliError::UnableToParse("rest_url", err.to_string()))?, - ); - - // lookup the address from onchain instead of deriving it - // if this is the rotated key, deriving it will outputs an incorrect address - let derived_address = account_address_from_public_key(&public_key); - let address = lookup_address(&client, derived_address, false).await?; - - profile_config.private_key = Some(private_key); - profile_config.public_key = Some(public_key.clone()); - profile_config.account = Some(address); - - // Create account if it doesn't exist (and there's a faucet) - // Check if account exists - let account_exists = match client.get_account(address).await { - Ok(_) => true, - Err(err) => { - if let RestError::Api(AptosErrorResponse { - error: - AptosError { - error_code: AptosErrorCode::ResourceNotFound, - .. - }, - .. - }) - | RestError::Api(AptosErrorResponse { - error: - AptosError { - error_code: AptosErrorCode::AccountNotFound, - .. - }, - .. - }) = err - { - false - } else { - return Err(CliError::UnexpectedError(format!( - "Failed to check if account exists: {:?}", - err - ))); - } - }, - }; - - // If you want to create a private key, but not fund the account, skipping the faucet is still possible - let maybe_faucet_url = if self.skip_faucet { - None - } else { - profile_config.faucet_url.as_ref() - }; - - if let Some(faucet_url) = maybe_faucet_url { - if account_exists { - eprintln!("Account {} has been already found onchain", address); - } else { - eprintln!( - "Account {} doesn't exist, creating it and funding it with {} Octas", - address, NUM_DEFAULT_OCTAS - ); - let hashes = fund_pub_key( - Url::parse(faucet_url) - .map_err(|err| CliError::UnableToParse("rest_url", err.to_string()))?, - // NUM_DEFAULT_OCTAS, - (public_key.clone()).to_string(), - ) - .await?; - wait_for_transactions(&client, hashes).await?; - eprintln!("Account {} funded successfully", address); - } - } else if account_exists { - eprintln!("Account {} has been already found onchain", address); - } else if network == Network::Mainnet { - eprintln!("Account {} does not exist, you will need to create and fund the account by transferring funds from another account", address); - } else { - eprintln!("Account {} has been initialized locally, but you must transfer coins to it to create the account onchain", address); - } - - // Ensure the loaded config has profiles setup for a possible empty file - if config.profiles.is_none() { - config.profiles = Some(BTreeMap::new()); - } - config - .profiles - .as_mut() - .expect("Must have profiles, as created above") - .insert(profile_name.to_string(), profile_config); - config.save()?; - eprintln!("\n---\nMovement CLI is now set up for account {} as profile {}! Run `movement --help` for more information about commands", address, self.profile_options.profile_name().unwrap_or(DEFAULT_PROFILE)); - Ok(()) - } -} - -impl InitTool { - /// Custom network created, which requires a REST URL - fn custom_network(&self, profile_config: &mut ProfileConfig) -> CliTypedResult<()> { - // Rest Endpoint - let rest_url = if let Some(ref rest_url) = self.rest_url { - eprintln!("Using command line argument for rest URL {}", rest_url); - Some(rest_url.to_string()) - } else { - let current = profile_config.rest_url.as_deref(); - eprintln!( - "Enter your rest endpoint [Current: {} | No input: Exit (or keep the existing if present)]", - current.unwrap_or("None"), - ); - let input = read_line("Rest endpoint")?; - let input = input.trim(); - if input.is_empty() { - if let Some(current) = current { - eprintln!("No rest url given, keeping the existing url..."); - Some(current.to_string()) - } else { - eprintln!("No rest url given, exiting..."); - return Err(CliError::AbortedError); - } - } else { - Some( - reqwest::Url::parse(input) - .map_err(|err| CliError::UnableToParse("Rest Endpoint", err.to_string()))? - .to_string(), - ) - } - }; - profile_config.rest_url = rest_url; - - // Faucet Endpoint - let faucet_url = if self.skip_faucet { - eprintln!("Not configuring a faucet because --skip-faucet was provided"); - None - } else if let Some(ref faucet_url) = self.faucet_url { - eprintln!("Using command line argument for faucet URL {}", faucet_url); - Some(faucet_url.to_string()) - } else { - let current = profile_config.faucet_url.as_deref(); - eprintln!( - "Enter your faucet endpoint [Current: {} | No input: Skip (or keep the existing one if present) | 'skip' to not use a faucet]", - current - .unwrap_or("None"), - ); - let input = read_line("Faucet endpoint")?; - let input = input.trim(); - if input.is_empty() { - if let Some(current) = current { - eprintln!("No faucet url given, keeping the existing url..."); - Some(current.to_string()) - } else { - eprintln!("No faucet url given, skipping faucet..."); - None - } - } else if input.to_lowercase() == "skip" { - eprintln!("Skipping faucet..."); - None - } else { - Some( - reqwest::Url::parse(input) - .map_err(|err| CliError::UnableToParse("Faucet Endpoint", err.to_string()))? - .to_string(), - ) - } - }; - profile_config.faucet_url = faucet_url; - Ok(()) - } -} - -/// A simplified list of all networks supported by the CLI -/// -/// Any command using this, will be simpler to setup as profiles -#[derive(Copy, Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] -pub enum Network { - Mainnet, - Testnet, - Devnet, - Local, - Custom, -} - -impl FromStr for Network { - type Err = CliError; - - fn from_str(s: &str) -> Result { - Ok(match s.to_lowercase().trim() { - "mainnet" => Self::Mainnet, - "testnet" => Self::Testnet, - "devnet" => Self::Devnet, - "local" => Self::Local, - "custom" => Self::Custom, - str => { - return Err(CliError::CommandArgumentError(format!( - "Invalid network {}. Must be one of [devnet, testnet, mainnet, local, custom]", - str - ))); - }, - }) - } -} - -impl Default for Network { - fn default() -> Self { - Self::Devnet - } -} diff --git a/m1/m1-cli/src/common/mod.rs b/m1/m1-cli/src/common/mod.rs deleted file mode 100644 index 1be94da1d..000000000 --- a/m1/m1-cli/src/common/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod init; -pub mod types; -pub mod utils; diff --git a/m1/m1-cli/src/common/types.rs b/m1/m1-cli/src/common/types.rs deleted file mode 100644 index bbaf52b0e..000000000 --- a/m1/m1-cli/src/common/types.rs +++ /dev/null @@ -1,1753 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - init::Network, - utils::{ - check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, - get_account_with_state, get_auth_key, get_sequence_number, prompt_yes_with_override, - read_from_file, start_logger, to_common_result, to_common_success_result, - write_to_file, write_to_file_with_opts, write_to_user_only_file, - }, - }, - config::GlobalConfig, - genesis::git::from_yaml, - move_tool::{ArgWithType, MemberId}, -}; -use aptos_crypto::{ - ed25519::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature}, - x25519, PrivateKey, ValidCryptoMaterial, ValidCryptoMaterialStringExt, -}; -use aptos_debugger::AptosDebugger; -use aptos_gas_profiling::FrameName; -use aptos_global_constants::adjust_gas_headroom; -use aptos_keygen::KeyGen; -use aptos_rest_client::{ - aptos_api_types::{HashValue, MoveType, ViewRequest}, - error::RestError, - Client, Transaction, -}; -use aptos_sdk::{transaction_builder::TransactionFactory, types::LocalAccount}; -use aptos_types::{ - chain_id::ChainId, - transaction::{ - authenticator::AuthenticationKey, EntryFunction, SignedTransaction, TransactionPayload, - TransactionStatus, - }, -}; -use async_trait::async_trait; -use clap::{ArgEnum, Parser}; -use hex::FromHexError; -use move_core_types::{account_address::AccountAddress, language_storage::TypeTag}; -use serde::{Deserialize, Serialize}; -#[cfg(unix)] -use std::os::unix::fs::OpenOptionsExt; -use std::{ - collections::BTreeMap, - convert::TryFrom, - fmt::{Debug, Display, Formatter}, - fs::OpenOptions, - path::{Path, PathBuf}, - str::FromStr, - time::{Duration, Instant, SystemTime, UNIX_EPOCH}, -}; -use reqwest::Url; -use thiserror::Error; - -pub const USER_AGENT: &str = concat!("movement-cli/", env!("CARGO_PKG_VERSION")); -const US_IN_SECS: u64 = 1_000_000; -const ACCEPTED_CLOCK_SKEW_US: u64 = 5 * US_IN_SECS; -pub const DEFAULT_EXPIRATION_SECS: u64 = 60; -pub const DEFAULT_PROFILE: &str = "default"; - -/// A common result to be returned to users -pub type CliResult = Result; - -/// A common result to remove need for typing `Result` -pub type CliTypedResult = Result; - -/// CLI Errors for reporting through telemetry and outputs -#[derive(Debug, Error)] -pub enum CliError { - #[error("Aborted command")] - AbortedError, - #[error("API error: {0}")] - ApiError(String), - #[error("Error (de)serializing '{0}': {1}")] - BCS(&'static str, #[source] bcs::Error), - #[error("Invalid arguments: {0}")] - CommandArgumentError(String), - #[error("Unable to load config: {0} {1}")] - ConfigLoadError(String, String), - #[error("Unable to find config {0}, have you run `movement init`?")] - ConfigNotFoundError(String), - #[error("Error accessing '{0}': {1}")] - IO(String, #[source] std::io::Error), - #[error("Move compilation failed: {0}")] - MoveCompilationError(String), - #[error("Move unit tests failed")] - MoveTestError, - #[error("Move Prover failed: {0}")] - MoveProverError(String), - #[error("Unable to parse '{0}': error: {1}")] - UnableToParse(&'static str, String), - #[error("Unable to read file '{0}', error: {1}")] - UnableToReadFile(String, String), - #[error("Unexpected error: {0}")] - UnexpectedError(String), - #[error("Simulation failed with status: {0}")] - SimulationError(String), - #[error("Coverage failed with status: {0}")] - CoverageError(String), -} - -impl CliError { - pub fn to_str(&self) -> &'static str { - match self { - CliError::AbortedError => "AbortedError", - CliError::ApiError(_) => "ApiError", - CliError::BCS(_, _) => "BCS", - CliError::CommandArgumentError(_) => "CommandArgumentError", - CliError::ConfigLoadError(_, _) => "ConfigLoadError", - CliError::ConfigNotFoundError(_) => "ConfigNotFoundError", - CliError::IO(_, _) => "IO", - CliError::MoveCompilationError(_) => "MoveCompilationError", - CliError::MoveTestError => "MoveTestError", - CliError::MoveProverError(_) => "MoveProverError", - CliError::UnableToParse(_, _) => "UnableToParse", - CliError::UnableToReadFile(_, _) => "UnableToReadFile", - CliError::UnexpectedError(_) => "UnexpectedError", - CliError::SimulationError(_) => "SimulationError", - CliError::CoverageError(_) => "CoverageError", - } - } -} - -impl From for CliError { - fn from(e: RestError) -> Self { - CliError::ApiError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: aptos_config::config::Error) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: aptos_github_client::Error) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: serde_yaml::Error) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: base64::DecodeError) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: std::string::FromUtf8Error) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: aptos_crypto::CryptoMaterialError) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: FromHexError) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: anyhow::Error) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: bcs::Error) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -/// Config saved to `.aptos/config.yaml` -#[derive(Debug, Serialize, Deserialize)] -pub struct CliConfig { - /// Map of profile configs - #[serde(skip_serializing_if = "Option::is_none")] - pub profiles: Option>, -} - -const CONFIG_FILE: &str = "config.yaml"; -const LEGACY_CONFIG_FILE: &str = "config.yml"; -pub const CONFIG_FOLDER: &str = ".movement"; - -/// An individual profile -#[derive(Debug, Default, Serialize, Deserialize)] -pub struct ProfileConfig { - #[serde(skip_serializing_if = "Option::is_none")] - pub network: Option, - /// Private key for commands. - #[serde(skip_serializing_if = "Option::is_none")] - pub private_key: Option, - /// Public key for commands - #[serde(skip_serializing_if = "Option::is_none")] - pub public_key: Option, - /// Account for commands - #[serde(skip_serializing_if = "Option::is_none")] - pub account: Option, - /// URL for the Aptos rest endpoint - #[serde(skip_serializing_if = "Option::is_none")] - pub rest_url: Option, - /// URL for the Faucet endpoint (if applicable) - #[serde(skip_serializing_if = "Option::is_none")] - pub faucet_url: Option, -} - -/// ProfileConfig but without the private parts -#[derive(Debug, Serialize)] -pub struct ProfileSummary { - pub has_private_key: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub public_key: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub account: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub rest_url: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub faucet_url: Option, -} - -impl From<&ProfileConfig> for ProfileSummary { - fn from(config: &ProfileConfig) -> Self { - ProfileSummary { - has_private_key: config.private_key.is_some(), - public_key: config.public_key.clone(), - account: config.account, - rest_url: config.rest_url.clone(), - faucet_url: config.faucet_url.clone(), - } - } -} - -impl Default for CliConfig { - fn default() -> Self { - CliConfig { - profiles: Some(BTreeMap::new()), - } - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] -pub enum ConfigSearchMode { - CurrentDir, - CurrentDirAndParents, -} - -impl CliConfig { - /// Checks if the config exists in the current working directory - pub fn config_exists(mode: ConfigSearchMode) -> bool { - if let Ok(folder) = Self::aptos_folder(mode) { - let config_file = folder.join(CONFIG_FILE); - let old_config_file = folder.join(LEGACY_CONFIG_FILE); - config_file.exists() || old_config_file.exists() - } else { - false - } - } - - /// Loads the config from the current working directory or one of its parents. - pub fn load(mode: ConfigSearchMode) -> CliTypedResult { - let folder = Self::aptos_folder(mode)?; - - let config_file = folder.join(CONFIG_FILE); - let old_config_file = folder.join(LEGACY_CONFIG_FILE); - if config_file.exists() { - from_yaml( - &String::from_utf8(read_from_file(config_file.as_path())?) - .map_err(CliError::from)?, - ) - } else if old_config_file.exists() { - from_yaml( - &String::from_utf8(read_from_file(old_config_file.as_path())?) - .map_err(CliError::from)?, - ) - } else { - Err(CliError::ConfigNotFoundError(format!( - "{}", - config_file.display() - ))) - } - } - - pub fn load_profile( - profile: Option<&str>, - mode: ConfigSearchMode, - ) -> CliTypedResult> { - let mut config = Self::load(mode)?; - - // If no profile was given, use `default` - if let Some(profile) = profile { - if let Some(account_profile) = config.remove_profile(profile) { - Ok(Some(account_profile)) - } else { - Err(CliError::CommandArgumentError(format!( - "Profile {} not found", - profile - ))) - } - } else { - Ok(config.remove_profile(DEFAULT_PROFILE)) - } - } - - pub fn remove_profile(&mut self, profile: &str) -> Option { - if let Some(ref mut profiles) = self.profiles { - profiles.remove(&profile.to_string()) - } else { - None - } - } - - /// Saves the config to ./.aptos/config.yaml - pub fn save(&self) -> CliTypedResult<()> { - let aptos_folder = Self::aptos_folder(ConfigSearchMode::CurrentDir)?; - - // Create if it doesn't exist - create_dir_if_not_exist(aptos_folder.as_path())?; - - // Save over previous config file - let config_file = aptos_folder.join(CONFIG_FILE); - let config_bytes = serde_yaml::to_string(&self).map_err(|err| { - CliError::UnexpectedError(format!("Failed to serialize config {}", err)) - })?; - write_to_user_only_file(&config_file, CONFIG_FILE, config_bytes.as_bytes())?; - - // As a cleanup, delete the old if it exists - let legacy_config_file = aptos_folder.join(LEGACY_CONFIG_FILE); - if legacy_config_file.exists() { - eprintln!("Removing legacy config file {}", LEGACY_CONFIG_FILE); - let _ = std::fs::remove_file(legacy_config_file); - } - Ok(()) - } - - /// Finds the current directory's .aptos folder - fn aptos_folder(mode: ConfigSearchMode) -> CliTypedResult { - let global_config = GlobalConfig::load()?; - global_config.get_config_location(mode) - } -} - -/// Types of Keys used by the blockchain -#[derive(ArgEnum, Clone, Copy, Debug)] -pub enum KeyType { - /// Ed25519 key used for signing - Ed25519, - /// X25519 key used for network handshakes and identity - X25519, - /// A BLS12381 key for consensus - Bls12381, -} - -impl Display for KeyType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let str = match self { - KeyType::Ed25519 => "ed25519", - KeyType::X25519 => "x25519", - KeyType::Bls12381 => "bls12381", - }; - write!(f, "{}", str) - } -} - -impl FromStr for KeyType { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "ed25519" => Ok(KeyType::Ed25519), - "x25519" => Ok(KeyType::X25519), - "bls12381" => Ok(KeyType::Bls12381), - _ => Err("Invalid key type: Must be one of [ed25519, x25519]"), - } - } -} - -#[derive(Debug, Default, Parser)] -pub struct ProfileOptions { - /// Profile to use from the CLI config - /// - /// This will be used to override associated settings such as - /// the REST URL, the Faucet URL, and the private key arguments. - /// - /// Defaults to "default" - #[clap(long)] - pub profile: Option, -} - -impl ProfileOptions { - pub fn account_address(&self) -> CliTypedResult { - let profile = self.profile()?; - if let Some(account) = profile.account { - return Ok(account); - } - - Err(CliError::ConfigNotFoundError( - self.profile - .clone() - .unwrap_or_else(|| DEFAULT_PROFILE.to_string()), - )) - } - - pub fn public_key(&self) -> CliTypedResult { - let profile = self.profile()?; - if let Some(public_key) = profile.public_key { - return Ok(public_key); - } - - Err(CliError::ConfigNotFoundError( - self.profile - .clone() - .unwrap_or_else(|| DEFAULT_PROFILE.to_string()), - )) - } - - pub fn profile_name(&self) -> Option<&str> { - self.profile.as_ref().map(|inner| inner.trim()) - } - - pub fn profile(&self) -> CliTypedResult { - if let Some(profile) = - CliConfig::load_profile(self.profile_name(), ConfigSearchMode::CurrentDirAndParents)? - { - return Ok(profile); - } - - Err(CliError::ConfigNotFoundError( - self.profile - .clone() - .unwrap_or_else(|| DEFAULT_PROFILE.to_string()), - )) - } -} - -/// Types of encodings used by the blockchain -#[derive(ArgEnum, Clone, Copy, Debug)] -pub enum EncodingType { - /// Binary Canonical Serialization - BCS, - /// Hex encoded e.g. 0xABCDE12345 - Hex, - /// Base 64 encoded - Base64, -} - -impl EncodingType { - /// Encodes `Key` into one of the `EncodingType`s - pub fn encode_key( - &self, - name: &'static str, - key: &Key, - ) -> CliTypedResult> { - Ok(match self { - EncodingType::Hex => hex::encode_upper(key.to_bytes()).into_bytes(), - EncodingType::BCS => bcs::to_bytes(key).map_err(|err| CliError::BCS(name, err))?, - EncodingType::Base64 => base64::encode(key.to_bytes()).into_bytes(), - }) - } - - /// Loads a key from a file - pub fn load_key( - &self, - name: &'static str, - path: &Path, - ) -> CliTypedResult { - self.decode_key(name, read_from_file(path)?) - } - - /// Decodes an encoded key given the known encoding - pub fn decode_key( - &self, - name: &'static str, - data: Vec, - ) -> CliTypedResult { - match self { - EncodingType::BCS => bcs::from_bytes(&data).map_err(|err| CliError::BCS(name, err)), - EncodingType::Hex => { - let hex_string = String::from_utf8(data)?; - Key::from_encoded_string(hex_string.trim()) - .map_err(|err| CliError::UnableToParse(name, err.to_string())) - } - EncodingType::Base64 => { - let string = String::from_utf8(data)?; - let bytes = base64::decode(string.trim()) - .map_err(|err| CliError::UnableToParse(name, err.to_string()))?; - Key::try_from(bytes.as_slice()).map_err(|err| { - CliError::UnableToParse(name, format!("Failed to parse key {:?}", err)) - }) - } - } - } -} - -#[derive(Clone, Debug, Parser)] -pub struct RngArgs { - /// The seed used for key generation, should be a 64 character hex string and only used for testing - /// - /// If a predictable random seed is used, the key that is produced will be insecure and easy - /// to reproduce. Please do not use this unless sufficient randomness is put into the random - /// seed. - #[clap(long)] - random_seed: Option, -} - -impl RngArgs { - pub fn from_seed(seed: [u8; 32]) -> RngArgs { - RngArgs { - random_seed: Some(hex::encode(seed)), - } - } - - pub fn from_string_seed(str: &str) -> RngArgs { - assert!(str.len() < 32); - - let mut seed = [0u8; 32]; - for (i, byte) in str.bytes().enumerate() { - seed[i] = byte; - } - - RngArgs { - random_seed: Some(hex::encode(seed)), - } - } - - /// Returns a key generator with the seed if given - pub fn key_generator(&self) -> CliTypedResult { - if let Some(ref seed) = self.random_seed { - // Strip 0x - let seed = seed.strip_prefix("0x").unwrap_or(seed); - let mut seed_slice = [0u8; 32]; - - hex::decode_to_slice(seed, &mut seed_slice)?; - Ok(KeyGen::from_seed(seed_slice)) - } else { - Ok(KeyGen::from_os_rng()) - } - } -} - -impl Default for EncodingType { - fn default() -> Self { - EncodingType::Hex - } -} - -impl Display for EncodingType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let str = match self { - EncodingType::BCS => "bcs", - EncodingType::Hex => "hex", - EncodingType::Base64 => "base64", - }; - write!(f, "{}", str) - } -} - -impl FromStr for EncodingType { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "hex" => Ok(EncodingType::Hex), - "bcs" => Ok(EncodingType::BCS), - "base64" => Ok(EncodingType::Base64), - _ => Err("Invalid encoding type"), - } - } -} - -/// An insertable option for use with prompts. -#[derive(Clone, Copy, Debug, Default, Parser, PartialEq, Eq)] -pub struct PromptOptions { - /// Assume yes for all yes/no prompts - #[clap(long, group = "prompt_options")] - pub assume_yes: bool, - /// Assume no for all yes/no prompts - #[clap(long, group = "prompt_options")] - pub assume_no: bool, -} - -impl PromptOptions { - pub fn yes() -> Self { - Self { - assume_yes: true, - assume_no: false, - } - } - - pub fn no() -> Self { - Self { - assume_yes: false, - assume_no: true, - } - } -} - -/// An insertable option for use with encodings. -#[derive(Debug, Default, Parser)] -pub struct EncodingOptions { - /// Encoding of data as one of [base64, bcs, hex] - #[clap(long, default_value_t = EncodingType::Hex)] - pub encoding: EncodingType, -} - -#[derive(Debug, Parser)] -pub struct PublicKeyInputOptions { - /// Ed25519 Public key input file name - /// - /// Mutually exclusive with `--public-key` - #[clap(long, group = "public_key_input", parse(from_os_str))] - public_key_file: Option, - /// Ed25519 Public key encoded in a type as shown in `encoding` - /// - /// Mutually exclusive with `--public-key-file` - #[clap(long, group = "public_key_input")] - public_key: Option, -} - -impl PublicKeyInputOptions { - pub fn from_key(key: &Ed25519PublicKey) -> PublicKeyInputOptions { - PublicKeyInputOptions { - public_key: Some(key.to_encoded_string().unwrap()), - public_key_file: None, - } - } -} - -impl ExtractPublicKey for PublicKeyInputOptions { - fn extract_public_key( - &self, - encoding: EncodingType, - profile: &ProfileOptions, - ) -> CliTypedResult { - if let Some(ref file) = self.public_key_file { - encoding.load_key("--public-key-file", file.as_path()) - } else if let Some(ref key) = self.public_key { - let key = key.as_bytes().to_vec(); - encoding.decode_key("--public-key", key) - } else if let Some(Some(public_key)) = CliConfig::load_profile( - profile.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|p| p.public_key) - { - Ok(public_key) - } else { - Err(CliError::CommandArgumentError( - "One of ['--public-key', '--public-key-file', '--profile'] must be used" - .to_string(), - )) - } - } -} - -pub trait ParsePrivateKey { - fn parse_private_key( - &self, - encoding: EncodingType, - private_key_file: Option, - private_key: Option, - ) -> CliTypedResult> { - if let Some(ref file) = private_key_file { - Ok(Some( - encoding.load_key("--private-key-file", file.as_path())?, - )) - } else if let Some(ref key) = private_key { - let key = key.as_bytes().to_vec(); - Ok(Some(encoding.decode_key("--private-key", key)?)) - } else { - Ok(None) - } - } -} - -#[derive(Debug, Default, Parser)] -pub struct PrivateKeyInputOptions { - /// Signing Ed25519 private key file path - /// - /// Encoded with type from `--encoding` - /// Mutually exclusive with `--private-key` - #[clap(long, group = "private_key_input", parse(from_os_str))] - private_key_file: Option, - /// Signing Ed25519 private key - /// - /// Encoded with type from `--encoding` - /// Mutually exclusive with `--private-key-file` - #[clap(long, group = "private_key_input")] - private_key: Option, -} - -impl ParsePrivateKey for PrivateKeyInputOptions {} - -impl PrivateKeyInputOptions { - pub fn from_private_key(private_key: &Ed25519PrivateKey) -> CliTypedResult { - Ok(PrivateKeyInputOptions { - private_key: Some( - private_key - .to_encoded_string() - .map_err(|err| CliError::UnexpectedError(err.to_string()))?, - ), - private_key_file: None, - }) - } - - pub fn from_x25519_private_key(private_key: &x25519::PrivateKey) -> CliTypedResult { - Ok(PrivateKeyInputOptions { - private_key: Some( - private_key - .to_encoded_string() - .map_err(|err| CliError::UnexpectedError(err.to_string()))?, - ), - private_key_file: None, - }) - } - - pub fn from_file(file: PathBuf) -> Self { - PrivateKeyInputOptions { - private_key: None, - private_key_file: Some(file), - } - } - - /// Extract private key from CLI args with fallback to config - pub fn extract_private_key_and_address( - &self, - encoding: EncodingType, - profile: &ProfileOptions, - maybe_address: Option, - ) -> CliTypedResult<(Ed25519PrivateKey, AccountAddress)> { - // Order of operations - // 1. CLI inputs - // 2. Profile - // 3. Derived - if let Some(key) = self.extract_private_key_cli(encoding)? { - // If we use the CLI inputs, then we should derive or use the address from the input - if let Some(address) = maybe_address { - Ok((key, address)) - } else { - let address = account_address_from_public_key(&key.public_key()); - Ok((key, address)) - } - } else if let Some((Some(key), maybe_config_address)) = CliConfig::load_profile( - profile.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|p| (p.private_key, p.account)) - { - match (maybe_address, maybe_config_address) { - (Some(address), _) => Ok((key, address)), - (_, Some(address)) => Ok((key, address)), - (None, None) => { - let address = account_address_from_public_key(&key.public_key()); - Ok((key, address)) - } - } - } else { - Err(CliError::CommandArgumentError( - "One of ['--private-key', '--private-key-file'] must be used".to_string(), - )) - } - } - - /// Extract private key from CLI args with fallback to config - pub fn extract_private_key( - &self, - encoding: EncodingType, - profile: &ProfileOptions, - ) -> CliTypedResult { - if let Some(key) = self.extract_private_key_cli(encoding)? { - Ok(key) - } else if let Some(Some(private_key)) = CliConfig::load_profile( - profile.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|p| p.private_key) - { - Ok(private_key) - } else { - Err(CliError::CommandArgumentError( - "One of ['--private-key', '--private-key-file'] must be used".to_string(), - )) - } - } - - /// Extract private key from CLI args - pub fn extract_private_key_cli( - &self, - encoding: EncodingType, - ) -> CliTypedResult> { - self.parse_private_key( - encoding, - self.private_key_file.clone(), - self.private_key.clone(), - ) - } -} - -impl ExtractPublicKey for PrivateKeyInputOptions { - fn extract_public_key( - &self, - encoding: EncodingType, - profile: &ProfileOptions, - ) -> CliTypedResult { - self.extract_private_key(encoding, profile) - .map(|private_key| private_key.public_key()) - } -} - -pub trait ExtractPublicKey { - fn extract_public_key( - &self, - encoding: EncodingType, - profile: &ProfileOptions, - ) -> CliTypedResult; -} - -pub fn account_address_from_public_key(public_key: &Ed25519PublicKey) -> AccountAddress { - let auth_key = AuthenticationKey::ed25519(public_key); - AccountAddress::new(*auth_key.derived_address()) -} - -#[derive(Debug, Parser)] -pub struct SaveFile { - /// Output file path - #[clap(long, parse(from_os_str))] - pub output_file: PathBuf, - - #[clap(flatten)] - pub prompt_options: PromptOptions, -} - -impl SaveFile { - /// Check if the key file exists already - pub fn check_file(&self) -> CliTypedResult<()> { - check_if_file_exists(self.output_file.as_path(), self.prompt_options) - } - - /// Save to the `output_file` - pub fn save_to_file(&self, name: &str, bytes: &[u8]) -> CliTypedResult<()> { - write_to_file(self.output_file.as_path(), name, bytes) - } - - /// Save to the `output_file` with restricted permissions (mode 0600) - pub fn save_to_file_confidential(&self, name: &str, bytes: &[u8]) -> CliTypedResult<()> { - let mut opts = OpenOptions::new(); - #[cfg(unix)] - opts.mode(0o600); - write_to_file_with_opts(self.output_file.as_path(), name, bytes, &mut opts) - } -} - -/// Options specific to using the Rest endpoint -#[derive(Debug, Default, Parser)] -pub struct RestOptions { - /// URL to a fullnode on the network - /// - /// Defaults to the URL in the `default` profile - #[clap(long)] - pub(crate) url: Option, - - /// Connection timeout in seconds, used for the REST endpoint of the fullnode - #[clap(long, default_value_t = DEFAULT_EXPIRATION_SECS, alias = "connection-timeout-s")] - pub connection_timeout_secs: u64, -} - -impl RestOptions { - pub fn new(url: Option, connection_timeout_secs: Option) -> Self { - RestOptions { - url, - connection_timeout_secs: connection_timeout_secs.unwrap_or(DEFAULT_EXPIRATION_SECS), - } - } - - /// Retrieve the URL from the profile or the command line - pub fn url(&self, profile: &ProfileOptions) -> CliTypedResult { - if let Some(ref url) = self.url { - Ok(url.clone()) - } else if let Some(Some(url)) = CliConfig::load_profile( - profile.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|p| p.rest_url) - { - reqwest::Url::parse(&url) - .map_err(|err| CliError::UnableToParse("Rest URL", err.to_string())) - } else { - Err(CliError::CommandArgumentError("No rest url given. Please add --url or add a rest_url to the .movement/config.yaml for the current profile".to_string())) - } - } - - pub fn client(&self, profile: &ProfileOptions) -> CliTypedResult { - Ok(Client::new_with_timeout_and_user_agent( - self.url(profile)?, - Duration::from_secs(self.connection_timeout_secs), - USER_AGENT, - )) - } - pub fn client_raw(&self, url: Url) -> CliTypedResult { - Ok(Client::new_with_timeout_and_user_agent( - url, - Duration::from_secs(self.connection_timeout_secs), - USER_AGENT, - )) - } -} - -/// Options for compiling a move package dir -#[derive(Debug, Clone, Parser)] -pub struct MovePackageDir { - /// Path to a move package (the folder with a Move.toml file) - #[clap(long, parse(from_os_str))] - pub package_dir: Option, - /// Path to save the compiled move package - /// - /// Defaults to `/build` - #[clap(long, parse(from_os_str))] - pub output_dir: Option, - /// Named addresses for the move binary - /// - /// Example: alice=0x1234, bob=0x5678 - /// - /// Note: This will fail if there are duplicates in the Move.toml file remove those first. - #[clap(long, parse(try_from_str = crate::common::utils::parse_map), default_value = "")] - pub(crate) named_addresses: BTreeMap, - - /// Skip pulling the latest git dependencies - /// - /// If you don't have a network connection, the compiler may fail due - /// to no ability to pull git dependencies. This will allow overriding - /// this for local development. - #[clap(long)] - pub(crate) skip_fetch_latest_git_deps: bool, - - /// Specify the version of the bytecode the compiler is going to emit. - #[clap(long)] - pub bytecode_version: Option, -} - -impl MovePackageDir { - pub fn new(package_dir: PathBuf) -> Self { - Self { - package_dir: Some(package_dir), - output_dir: None, - named_addresses: Default::default(), - skip_fetch_latest_git_deps: true, - bytecode_version: None, - } - } - - pub fn get_package_path(&self) -> CliTypedResult { - dir_default_to_current(self.package_dir.clone()) - } - - /// Retrieve the NamedAddresses, resolving all the account addresses accordingly - pub fn named_addresses(&self) -> BTreeMap { - self.named_addresses - .clone() - .into_iter() - .map(|(key, value)| (key, value.account_address)) - .collect() - } - - pub fn add_named_address(&mut self, key: String, value: String) { - self.named_addresses - .insert(key, AccountAddressWrapper::from_str(&value).unwrap()); - } -} - -/// A wrapper around `AccountAddress` to be more flexible from strings than AccountAddress -#[derive(Clone, Copy, Debug)] -pub struct AccountAddressWrapper { - pub account_address: AccountAddress, -} - -impl FromStr for AccountAddressWrapper { - type Err = CliError; - - fn from_str(s: &str) -> Result { - Ok(AccountAddressWrapper { - account_address: load_account_arg(s)?, - }) - } -} - -/// Loads an account arg and allows for naming based on profiles -pub fn load_account_arg(str: &str) -> Result { - if str.starts_with("0x") { - AccountAddress::from_hex_literal(str).map_err(|err| { - CliError::CommandArgumentError(format!("Failed to parse AccountAddress {}", err)) - }) - } else if let Ok(account_address) = AccountAddress::from_str(str) { - Ok(account_address) - } else if let Some(Some(account_address)) = - CliConfig::load_profile(Some(str), ConfigSearchMode::CurrentDirAndParents)? - .map(|p| p.account) - { - Ok(account_address) - } else if let Some(Some(private_key)) = - CliConfig::load_profile(Some(str), ConfigSearchMode::CurrentDirAndParents)? - .map(|p| p.private_key) - { - let public_key = private_key.public_key(); - Ok(account_address_from_public_key(&public_key)) - } else { - Err(CliError::CommandArgumentError( - "'--account' or '--profile' after using movement init must be provided".to_string(), - )) - } -} - -/// A wrapper around `AccountAddress` to allow for "_" -#[derive(Clone, Copy, Debug)] -pub struct MoveManifestAccountWrapper { - pub account_address: Option, -} - -impl FromStr for MoveManifestAccountWrapper { - type Err = CliError; - - fn from_str(s: &str) -> Result { - Ok(MoveManifestAccountWrapper { - account_address: load_manifest_account_arg(s)?, - }) - } -} - -/// Loads an account arg and allows for naming based on profiles and "_" -pub fn load_manifest_account_arg(str: &str) -> Result, CliError> { - if str == "_" { - Ok(None) - } else if str.starts_with("0x") { - AccountAddress::from_hex_literal(str) - .map(Some) - .map_err(|err| { - CliError::CommandArgumentError(format!("Failed to parse AccountAddress {}", err)) - }) - } else if let Ok(account_address) = AccountAddress::from_str(str) { - Ok(Some(account_address)) - } else if let Some(Some(private_key)) = - CliConfig::load_profile(Some(str), ConfigSearchMode::CurrentDirAndParents)? - .map(|p| p.private_key) - { - let public_key = private_key.public_key(); - Ok(Some(account_address_from_public_key(&public_key))) - } else { - Err(CliError::CommandArgumentError( - "Invalid Move manifest account address".to_string(), - )) - } -} - -/// A common trait for all CLI commands to have consistent outputs -#[async_trait] -pub trait CliCommand: Sized + Send { - /// Returns a name for logging purposes - fn command_name(&self) -> &'static str; - - /// Executes the command, returning a command specific type - async fn execute(self) -> CliTypedResult; - - /// Executes the command, and serializes it to the common JSON output type - async fn execute_serialized(self) -> CliResult { - let command_name = self.command_name(); - start_logger(); - let start_time = Instant::now(); - to_common_result(command_name, start_time, self.execute().await).await - } - - /// Same as execute serialized without setting up logging - async fn execute_serialized_without_logger(self) -> CliResult { - let command_name = self.command_name(); - let start_time = Instant::now(); - to_common_result(command_name, start_time, self.execute().await).await - } - - /// Executes the command, and throws away Ok(result) for the string Success - async fn execute_serialized_success(self) -> CliResult { - start_logger(); - let command_name = self.command_name(); - let start_time = Instant::now(); - to_common_success_result(command_name, start_time, self.execute().await).await - } -} - -/// A shortened transaction output -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct TransactionSummary { - pub transaction_hash: HashValue, - #[serde(skip_serializing_if = "Option::is_none")] - pub gas_used: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub gas_unit_price: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub pending: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub sender: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub sequence_number: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub success: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub timestamp_us: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub version: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub vm_status: Option, -} - -impl From for TransactionSummary { - fn from(transaction: Transaction) -> Self { - TransactionSummary::from(&transaction) - } -} - -impl From<&Transaction> for TransactionSummary { - fn from(transaction: &Transaction) -> Self { - match transaction { - Transaction::PendingTransaction(txn) => TransactionSummary { - transaction_hash: txn.hash, - pending: Some(true), - sender: Some(*txn.request.sender.inner()), - sequence_number: Some(txn.request.sequence_number.0), - gas_used: None, - gas_unit_price: None, - success: None, - version: None, - vm_status: None, - timestamp_us: None, - }, - Transaction::UserTransaction(txn) => TransactionSummary { - transaction_hash: txn.info.hash, - sender: Some(*txn.request.sender.inner()), - gas_used: Some(txn.info.gas_used.0), - gas_unit_price: Some(txn.request.gas_unit_price.0), - success: Some(txn.info.success), - version: Some(txn.info.version.0), - vm_status: Some(txn.info.vm_status.clone()), - sequence_number: Some(txn.request.sequence_number.0), - timestamp_us: Some(txn.timestamp.0), - pending: None, - }, - Transaction::GenesisTransaction(txn) => TransactionSummary { - transaction_hash: txn.info.hash, - success: Some(txn.info.success), - version: Some(txn.info.version.0), - vm_status: Some(txn.info.vm_status.clone()), - sender: None, - gas_used: None, - gas_unit_price: None, - pending: None, - sequence_number: None, - timestamp_us: None, - }, - Transaction::BlockMetadataTransaction(txn) => TransactionSummary { - transaction_hash: txn.info.hash, - success: Some(txn.info.success), - version: Some(txn.info.version.0), - vm_status: Some(txn.info.vm_status.clone()), - timestamp_us: Some(txn.timestamp.0), - sender: None, - gas_used: None, - gas_unit_price: None, - pending: None, - sequence_number: None, - }, - Transaction::StateCheckpointTransaction(txn) => TransactionSummary { - transaction_hash: txn.info.hash, - success: Some(txn.info.success), - version: Some(txn.info.version.0), - vm_status: Some(txn.info.vm_status.clone()), - timestamp_us: Some(txn.timestamp.0), - sender: None, - gas_used: None, - gas_unit_price: None, - pending: None, - sequence_number: None, - }, - } - } -} - -/// A summary of a `WriteSetChange` for easy printing -#[derive(Clone, Debug, Default, Serialize)] -pub struct ChangeSummary { - #[serde(skip_serializing_if = "Option::is_none")] - address: Option, - #[serde(skip_serializing_if = "Option::is_none")] - data: Option, - event: &'static str, - #[serde(skip_serializing_if = "Option::is_none")] - handle: Option, - #[serde(skip_serializing_if = "Option::is_none")] - key: Option, - #[serde(skip_serializing_if = "Option::is_none")] - module: Option, - #[serde(skip_serializing_if = "Option::is_none")] - resource: Option, - #[serde(skip_serializing_if = "Option::is_none")] - value: Option, -} - -#[derive(Debug, Default, Parser)] -pub struct FaucetOptions { - /// URL for the faucet endpoint e.g. `https://faucet.devnet.aptoslabs.com` - #[clap(long)] - faucet_url: Option, -} - -impl FaucetOptions { - pub fn new(faucet_url: Option) -> Self { - FaucetOptions { faucet_url } - } - - pub fn faucet_url(&self, profile: &ProfileOptions) -> CliTypedResult { - if let Some(ref faucet_url) = self.faucet_url { - Ok(faucet_url.clone()) - } else if let Some(Some(url)) = CliConfig::load_profile( - profile.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|profile| profile.faucet_url) - { - reqwest::Url::parse(&url) - .map_err(|err| CliError::UnableToParse("config faucet_url", err.to_string())) - } else { - Err(CliError::CommandArgumentError("No faucet given. Please add --faucet-url or add a faucet URL to the .movement/config.yaml for the current profile".to_string())) - } - } -} - -/// Gas price options for manipulating how to prioritize transactions -#[derive(Debug, Eq, Parser, PartialEq)] -pub struct GasOptions { - /// Gas multiplier per unit of gas - /// - /// The amount of Octas (10^-8 APT) used for a transaction is equal - /// to (gas unit price * gas used). The gas_unit_price can - /// be used as a multiplier for the amount of Octas willing - /// to be paid for a transaction. This will prioritize the - /// transaction with a higher gas unit price. - /// - /// Without a value, it will determine the price based on the current estimated price - #[clap(long)] - pub gas_unit_price: Option, - /// Maximum amount of gas units to be used to send this transaction - /// - /// The maximum amount of gas units willing to pay for the transaction. - /// This is the (max gas in Octas / gas unit price). - /// - /// For example if I wanted to pay a maximum of 100 Octas, I may have the - /// max gas set to 100 if the gas unit price is 1. If I want it to have a - /// gas unit price of 2, the max gas would need to be 50 to still only have - /// a maximum price of 100 Octas. - /// - /// Without a value, it will determine the price based on simulating the current transaction - #[clap(long)] - pub max_gas: Option, - /// Number of seconds to expire the transaction - /// - /// This is the number of seconds from the current local computer time. - #[clap(long, default_value_t = DEFAULT_EXPIRATION_SECS)] - pub expiration_secs: u64, -} - -impl Default for GasOptions { - fn default() -> Self { - GasOptions { - gas_unit_price: None, - max_gas: None, - expiration_secs: DEFAULT_EXPIRATION_SECS, - } - } -} - -/// Common options for interacting with an account for a validator -#[derive(Debug, Default, Parser)] -pub struct TransactionOptions { - /// Sender account address - /// - /// This allows you to override the account address from the derived account address - /// in the event that the authentication key was rotated or for a resource account - #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] - pub(crate) sender_account: Option, - - #[clap(flatten)] - pub(crate) private_key_options: PrivateKeyInputOptions, - #[clap(flatten)] - pub(crate) encoding_options: EncodingOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) gas_options: GasOptions, - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, - - /// If this option is set, simulate the transaction locally using the debugger and generate - /// flamegraphs that reflect the gas usage. - #[clap(long)] - pub(crate) profile_gas: bool, -} - -impl TransactionOptions { - /// Builds a rest client - fn rest_client(&self) -> CliTypedResult { - self.rest_options.client(&self.profile_options) - } - - /// Retrieves the public key and the associated address - /// TODO: Cache this information - pub fn get_key_and_address(&self) -> CliTypedResult<(Ed25519PrivateKey, AccountAddress)> { - self.private_key_options.extract_private_key_and_address( - self.encoding_options.encoding, - &self.profile_options, - self.sender_account, - ) - } - - pub fn sender_address(&self) -> CliTypedResult { - Ok(self.get_key_and_address()?.1) - } - - /// Gets the auth key by account address. We need to fetch the auth key from Rest API rather than creating an - /// auth key out of the public key. - pub(crate) async fn auth_key( - &self, - sender_address: AccountAddress, - ) -> CliTypedResult { - let client = self.rest_client()?; - get_auth_key(&client, sender_address).await - } - - pub async fn sequence_number(&self, sender_address: AccountAddress) -> CliTypedResult { - let client = self.rest_client()?; - get_sequence_number(&client, sender_address).await - } - - pub async fn view(&self, payload: ViewRequest) -> CliTypedResult> { - let client = self.rest_client()?; - Ok(client.view(&payload, None).await?.into_inner()) - } - - /// Submit a transaction - pub async fn submit_transaction( - &self, - payload: TransactionPayload, - ) -> CliTypedResult { - let client = self.rest_client()?; - let (sender_key, sender_address) = self.get_key_and_address()?; - - // Ask to confirm price if the gas unit price is estimated above the lowest value when - // it is automatically estimated - let ask_to_confirm_price; - let gas_unit_price = if let Some(gas_unit_price) = self.gas_options.gas_unit_price { - ask_to_confirm_price = false; - gas_unit_price - } else { - let gas_unit_price = client.estimate_gas_price().await?.into_inner().gas_estimate; - - ask_to_confirm_price = true; - gas_unit_price - }; - - // Get sequence number for account - let (account, state) = get_account_with_state(&client, sender_address).await?; - let sequence_number = account.sequence_number; - - // Retrieve local time, and ensure it's within an expected skew of the blockchain - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .map_err(|err| CliError::UnexpectedError(err.to_string()))? - .as_secs(); - let now_usecs = now * US_IN_SECS; - - // Warn local user that clock is skewed behind the blockchain. - // There will always be a little lag from real time to blockchain time - if now_usecs < state.timestamp_usecs - ACCEPTED_CLOCK_SKEW_US { - eprintln!("Local clock is is skewed from blockchain clock. Clock is more than {} seconds behind the blockchain {}", ACCEPTED_CLOCK_SKEW_US, state.timestamp_usecs / US_IN_SECS); - } - let expiration_time_secs = now + self.gas_options.expiration_secs; - - let chain_id = ChainId::new(state.chain_id); - // TODO: Check auth key against current private key and provide a better message - - let max_gas = if let Some(max_gas) = self.gas_options.max_gas { - // If the gas unit price was estimated ask, but otherwise you've chosen hwo much you want to spend - if ask_to_confirm_price { - let message = format!("Do you want to submit transaction for a maximum of {} Octas at a gas unit price of {} Octas?", max_gas * gas_unit_price, gas_unit_price); - prompt_yes_with_override(&message, self.prompt_options)?; - } - max_gas - } else { - let transaction_factory = - TransactionFactory::new(chain_id).with_gas_unit_price(gas_unit_price); - - let unsigned_transaction = transaction_factory - .payload(payload.clone()) - .sender(sender_address) - .sequence_number(sequence_number) - .expiration_timestamp_secs(expiration_time_secs) - .build(); - - let signed_transaction = SignedTransaction::new( - unsigned_transaction, - sender_key.public_key(), - Ed25519Signature::try_from([0u8; 64].as_ref()).unwrap(), - ); - - let txns = client - .simulate_with_gas_estimation(&signed_transaction, true, false) - .await? - .into_inner(); - let simulated_txn = txns.first().unwrap(); - - // Check if the transaction will pass, if it doesn't then fail - if !simulated_txn.info.success { - return Err(CliError::SimulationError( - simulated_txn.info.vm_status.clone(), - )); - } - - // Take the gas used and use a headroom factor on it - let gas_used = simulated_txn.info.gas_used.0; - let adjusted_max_gas = - adjust_gas_headroom(gas_used, simulated_txn.request.max_gas_amount.0); - - // Ask if you want to accept the estimate amount - let upper_cost_bound = adjusted_max_gas * gas_unit_price; - let lower_cost_bound = gas_used * gas_unit_price; - let message = format!( - "Do you want to submit a transaction for a range of [{} - {}] Octas at a gas unit price of {} Octas?", - lower_cost_bound, - upper_cost_bound, - gas_unit_price); - prompt_yes_with_override(&message, self.prompt_options)?; - adjusted_max_gas - }; - - // Sign and submit transaction - let transaction_factory = TransactionFactory::new(chain_id) - .with_gas_unit_price(gas_unit_price) - .with_max_gas_amount(max_gas) - .with_transaction_expiration_time(self.gas_options.expiration_secs); - let sender_account = &mut LocalAccount::new(sender_address, sender_key, sequence_number); - let transaction = - sender_account.sign_with_transaction_builder(transaction_factory.payload(payload)); - let response = client - .submit_and_wait(&transaction) - .await - .map_err(|err| CliError::ApiError(err.to_string()))?; - - Ok(response.into_inner()) - } - - /// Simulate the transaction locally using the debugger, with the gas profiler enabled. - pub async fn profile_gas( - &self, - payload: TransactionPayload, - ) -> CliTypedResult { - println!(); - println!("Simulating transaction locally with the gas profiler..."); - println!("This is still experimental so results may be inaccurate."); - - let client = self.rest_client()?; - - // Fetch the chain states required for the simulation - // TODO(Gas): get the following from the chain - const DEFAULT_GAS_UNIT_PRICE: u64 = 100; - const DEFAULT_MAX_GAS: u64 = 2_000_000; - - let (sender_key, sender_address) = self.get_key_and_address()?; - let gas_unit_price = self - .gas_options - .gas_unit_price - .unwrap_or(DEFAULT_GAS_UNIT_PRICE); - let (account, state) = get_account_with_state(&client, sender_address).await?; - let version = state.version; - let chain_id = ChainId::new(state.chain_id); - let sequence_number = account.sequence_number; - - let balance = client - .get_account_balance_at_version(sender_address, version) - .await - .map_err(|err| CliError::ApiError(err.to_string()))? - .into_inner(); - - let max_gas = self.gas_options.max_gas.unwrap_or_else(|| { - if gas_unit_price == 0 { - DEFAULT_MAX_GAS - } else { - std::cmp::min(balance.coin.value.0 / gas_unit_price, DEFAULT_MAX_GAS) - } - }); - - // Create and sign the transaction - let transaction_factory = TransactionFactory::new(chain_id) - .with_gas_unit_price(gas_unit_price) - .with_max_gas_amount(max_gas) - .with_transaction_expiration_time(self.gas_options.expiration_secs); - let sender_account = &mut LocalAccount::new(sender_address, sender_key, sequence_number); - let transaction = - sender_account.sign_with_transaction_builder(transaction_factory.payload(payload)); - let hash = transaction.clone().committed_hash(); - - // Execute the transaction using the debugger - let debugger = AptosDebugger::rest_client(client).unwrap(); - let res = debugger.execute_transaction_at_version_with_gas_profiler(version, transaction); - let (vm_status, output, gas_log) = res.map_err(|err| { - CliError::UnexpectedError(format!("failed to simulate txn with gas profiler: {}", err)) - })?; - - // Generate the file name for the flamegraphs - let entry_point = gas_log.entry_point(); - - let human_readable_name = match entry_point { - FrameName::Script => "script".to_string(), - FrameName::Function { - module_id, name, .. - } => { - let addr_short = module_id.address().short_str_lossless(); - let addr_truncated = if addr_short.len() > 4 { - &addr_short[..4] - } else { - addr_short.as_str() - }; - format!("0x{}-{}-{}", addr_truncated, module_id.name(), name) - } - }; - let raw_file_name = format!("txn-{}-{}", hash, human_readable_name); - - // Create the directory if it does not exist yet. - let dir: &Path = Path::new("gas-profiling"); - - macro_rules! create_dir { - () => { - if let Err(err) = std::fs::create_dir(dir) { - if err.kind() != std::io::ErrorKind::AlreadyExists { - return Err(CliError::UnexpectedError(format!( - "failed to create directory {}", - dir.display() - ))); - } - } - }; - } - - // Generate the execution & IO flamegraph. - println!(); - match gas_log.to_flamegraph(format!("Transaction {} -- Execution & IO", hash))? { - Some(graph_bytes) => { - create_dir!(); - let graph_file_path = Path::join(dir, format!("{}.exec_io.svg", raw_file_name)); - std::fs::write(&graph_file_path, graph_bytes).map_err(|err| { - CliError::UnexpectedError(format!( - "Failed to write flamegraph to file {} : {:?}", - graph_file_path.display(), - err - )) - })?; - println!( - "Execution & IO Gas flamegraph saved to {}", - graph_file_path.display() - ); - } - None => { - println!("Skipped generating execution & IO flamegraph"); - } - } - - // Generate the storage fee flamegraph. - match gas_log - .storage - .to_flamegraph(format!("Transaction {} -- Storage Fee", hash))? - { - Some(graph_bytes) => { - create_dir!(); - let graph_file_path = Path::join(dir, format!("{}.storage.svg", raw_file_name)); - std::fs::write(&graph_file_path, graph_bytes).map_err(|err| { - CliError::UnexpectedError(format!( - "Failed to write flamegraph to file {} : {:?}", - graph_file_path.display(), - err - )) - })?; - println!( - "Storage fee flamegraph saved to {}", - graph_file_path.display() - ); - } - None => { - println!("Skipped generating storage fee flamegraph"); - } - } - - println!(); - - // Generate the transaction summary - - // TODO(Gas): double check if this is correct. - let success = match output.status() { - TransactionStatus::Keep(exec_status) => Some(exec_status.is_success()), - TransactionStatus::Discard(_) | TransactionStatus::Retry => None, - }; - - Ok(TransactionSummary { - transaction_hash: hash.into(), - gas_used: Some(output.gas_used()), - gas_unit_price: Some(gas_unit_price), - pending: None, - sender: Some(sender_address), - sequence_number: None, // The transaction is not comitted so there is no new sequence number. - success, - timestamp_us: None, - version: Some(version), // The transaction is not comitted so there is no new version. - vm_status: Some(vm_status.to_string()), - }) - } - - pub async fn estimate_gas_price(&self) -> CliTypedResult { - let client = self.rest_client()?; - client - .estimate_gas_price() - .await - .map(|inner| inner.into_inner().gas_estimate) - .map_err(|err| { - CliError::UnexpectedError(format!( - "Failed to retrieve gas price estimate {:?}", - err - )) - }) - } -} - -#[derive(Parser)] -pub struct OptionalPoolAddressArgs { - /// Address of the Staking pool - /// - /// Defaults to the profile's `AccountAddress` - #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] - pub(crate) pool_address: Option, -} - -#[derive(Parser)] -pub struct PoolAddressArgs { - /// Address of the Staking pool - #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] - pub(crate) pool_address: AccountAddress, -} - -// This struct includes TypeInfo (account_address, module_name, and struct_name) -// and RotationProofChallenge-specific information (sequence_number, originator, current_auth_key, and new_public_key) -// Since the struct RotationProofChallenge is defined in "0x1::account::RotationProofChallenge", -// we will be passing in "0x1" to `account_address`, "account" to `module_name`, and "RotationProofChallenge" to `struct_name` -// Originator refers to the user's address -#[derive(Serialize, Deserialize)] -pub struct RotationProofChallenge { - // Should be `CORE_CODE_ADDRESS` - pub account_address: AccountAddress, - // Should be `account` - pub module_name: String, - // Should be `RotationProofChallenge` - pub struct_name: String, - pub sequence_number: u64, - pub originator: AccountAddress, - pub current_auth_key: AccountAddress, - pub new_public_key: Vec, -} - -#[derive(Debug, Parser)] -/// This is used for both entry functions and scripts. -pub struct ArgWithTypeVec { - /// Arguments combined with their type separated by spaces. - /// - /// Supported types [address, bool, hex, string, u8, u16, u32, u64, u128, u256, raw] - /// - /// Vectors may be specified using JSON array literal syntax (you may need to escape this with - /// quotes based on your shell interpreter) - /// - /// Example: `address:0x1 bool:true u8:0 u256:1234 "bool:[true, false]" 'address:[["0xace", "0xbee"], []]'` - /// - /// Vector is wrapped in a reusable struct for uniform CLI documentation. - #[clap(long, multiple_values = true)] - pub(crate) args: Vec, -} - -/// Common options for constructing an entry function transaction payload. -#[derive(Debug, Parser)] -pub struct EntryFunctionArguments { - /// Function name as `
::::` - /// - /// Example: `0x842ed41fad9640a2ad08fdd7d3e4f7f505319aac7d67e1c0dd6a7cce8732c7e3::message::set_message` - #[clap(long)] - pub function_id: MemberId, - - #[clap(flatten)] - pub(crate) arg_vec: ArgWithTypeVec, - - /// TypeTag arguments separated by spaces. - /// - /// Example: `u8 u16 u32 u64 u128 u256 bool address vector signer` - #[clap(long, multiple_values = true)] - pub type_args: Vec, -} - -impl EntryFunctionArguments { - /// Construct and return an entry function payload from function_id, args, and type_args. - pub fn create_entry_function_payload(self) -> CliTypedResult { - let args: Vec> = self - .arg_vec - .args - .into_iter() - .map(|arg_with_type| arg_with_type.arg) - .collect(); - - let mut parsed_type_args: Vec = Vec::new(); - // These TypeArgs are used for generics - for type_arg in self.type_args.into_iter() { - let type_tag = TypeTag::try_from(type_arg.clone()) - .map_err(|err| CliError::UnableToParse("--type-args", err.to_string()))?; - parsed_type_args.push(type_tag) - } - - Ok(EntryFunction::new( - self.function_id.module_id, - self.function_id.member_id, - parsed_type_args, - args, - )) - } -} - -/// Common options for interactions with a multisig account. -#[derive(Clone, Debug, Parser, Serialize)] -pub struct MultisigAccount { - /// The address of the multisig account to interact with. - #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] - pub(crate) multisig_address: AccountAddress, -} diff --git a/m1/m1-cli/src/common/utils.rs b/m1/m1-cli/src/common/utils.rs deleted file mode 100644 index 126afde04..000000000 --- a/m1/m1-cli/src/common/utils.rs +++ /dev/null @@ -1,507 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::types::{ - account_address_from_public_key, CliError, CliTypedResult, PromptOptions, - TransactionOptions, TransactionSummary, - }, - config::GlobalConfig, - CliResult, -}; -use aptos_build_info::build_information; -use aptos_crypto::ed25519::{Ed25519PrivateKey, Ed25519PublicKey}; -use aptos_keygen::KeyGen; -use aptos_logger::{debug, Level}; -use aptos_rest_client::{aptos_api_types::HashValue, Account, Client, State}; -use aptos_telemetry::service::telemetry_is_disabled; -use aptos_types::{ - account_address::create_multisig_account_address, - chain_id::ChainId, - transaction::{authenticator::AuthenticationKey, TransactionPayload}, -}; -use itertools::Itertools; -use move_core_types::account_address::AccountAddress; -use reqwest::Url; -use serde::Serialize; -#[cfg(unix)] -use std::os::unix::fs::OpenOptionsExt; -use std::{ - collections::BTreeMap, - env, - fs::OpenOptions, - io::Write, - path::{Path, PathBuf}, - str::FromStr, - time::{Duration, Instant, SystemTime}, -}; - -/// Prompts for confirmation until a yes or no is given explicitly -pub fn prompt_yes(prompt: &str) -> bool { - let mut result: Result = Err(()); - - // Read input until a yes or a no is given - while result.is_err() { - println!("{} [yes/no] >", prompt); - let mut input = String::new(); - if std::io::stdin().read_line(&mut input).is_err() { - continue; - } - result = match input.trim().to_lowercase().as_str() { - "yes" | "y" => Ok(true), - "no" | "n" => Ok(false), - _ => Err(()), - }; - } - result.unwrap() -} - -/// Convert any successful response to Success -pub async fn to_common_success_result( - command: &str, - start_time: Instant, - result: CliTypedResult, -) -> CliResult { - to_common_result(command, start_time, result.map(|_| "Success")).await -} - -/// For pretty printing outputs in JSON -pub async fn to_common_result( - command: &str, - start_time: Instant, - result: CliTypedResult, -) -> CliResult { - let latency = start_time.elapsed(); - let is_err = result.is_err(); - - if !telemetry_is_disabled() { - let error = if let Err(ref error) = result { - // Only print the error type - Some(error.to_str()) - } else { - None - }; - send_telemetry_event(command, latency, !is_err, error).await; - } - - let result: ResultWrapper = result.into(); - let string = serde_json::to_string_pretty(&result).unwrap(); - if is_err { - Err(string) - } else { - Ok(string) - } -} - -pub fn cli_build_information() -> BTreeMap { - build_information!() -} - -/// Sends a telemetry event about the CLI build, command and result -async fn send_telemetry_event( - command: &str, - latency: Duration, - success: bool, - error: Option<&str>, -) { - // Collect the build information - let build_information = cli_build_information(); - - // Send the event - aptos_telemetry::cli_metrics::send_cli_telemetry_event( - build_information, - command.into(), - latency, - success, - error, - ) - .await; -} - -/// A result wrapper for displaying either a correct execution result or an error. -/// -/// The purpose of this is to have a pretty easy to recognize JSON output format e.g. -/// -/// { -/// "Result":{ -/// "encoded":{ ... } -/// } -/// } -/// -/// { -/// "Error":"Failed to run command" -/// } -/// -#[derive(Debug, Serialize)] -enum ResultWrapper { - Result(T), - Error(String), -} - -impl From> for ResultWrapper { - fn from(result: CliTypedResult) -> Self { - match result { - Ok(inner) => ResultWrapper::Result(inner), - Err(inner) => ResultWrapper::Error(inner.to_string()), - } - } -} - -/// Checks if a file exists, being overridden by `PromptOptions` -pub fn check_if_file_exists(file: &Path, prompt_options: PromptOptions) -> CliTypedResult<()> { - if file.exists() { - prompt_yes_with_override( - &format!( - "{:?} already exists, are you sure you want to overwrite it?", - file.as_os_str(), - ), - prompt_options, - )? - } - - Ok(()) -} - -pub fn prompt_yes_with_override(prompt: &str, prompt_options: PromptOptions) -> CliTypedResult<()> { - if prompt_options.assume_no { - return Err(CliError::AbortedError); - } else if prompt_options.assume_yes { - return Ok(()); - } - - let is_yes = if let Some(response) = GlobalConfig::load()?.get_default_prompt_response() { - response - } else { - prompt_yes(prompt) - }; - - if is_yes { - Ok(()) - } else { - Err(CliError::AbortedError) - } -} - -pub fn read_from_file(path: &Path) -> CliTypedResult> { - std::fs::read(path) - .map_err(|e| CliError::UnableToReadFile(format!("{}", path.display()), e.to_string())) -} - -/// Write a `&[u8]` to a file -pub fn write_to_file(path: &Path, name: &str, bytes: &[u8]) -> CliTypedResult<()> { - write_to_file_with_opts(path, name, bytes, &mut OpenOptions::new()) -} - -/// Write a User only read / write file -pub fn write_to_user_only_file(path: &Path, name: &str, bytes: &[u8]) -> CliTypedResult<()> { - let mut opts = OpenOptions::new(); - #[cfg(unix)] - opts.mode(0o600); - write_to_file_with_opts(path, name, bytes, &mut opts) -} - -/// Write a `&[u8]` to a file with the given options -pub fn write_to_file_with_opts( - path: &Path, - name: &str, - bytes: &[u8], - opts: &mut OpenOptions, -) -> CliTypedResult<()> { - let mut file = opts - .write(true) - .create(true) - .truncate(true) - .open(path) - .map_err(|e| CliError::IO(name.to_string(), e))?; - file.write_all(bytes) - .map_err(|e| CliError::IO(name.to_string(), e)) -} - -/// Appends a file extension to a `Path` without overwriting the original extension. -pub fn append_file_extension( - file: &Path, - appended_extension: &'static str, -) -> CliTypedResult { - let extension = file - .extension() - .map(|extension| extension.to_str().unwrap_or_default()); - if let Some(extension) = extension { - Ok(file.with_extension(extension.to_owned() + "." + appended_extension)) - } else { - Ok(file.with_extension(appended_extension)) - } -} - -/// Retrieves account resource from the rest client -pub async fn get_account( - client: &aptos_rest_client::Client, - address: AccountAddress, -) -> CliTypedResult { - let account_response = client - .get_account(address) - .await - .map_err(|err| CliError::ApiError(err.to_string()))?; - Ok(account_response.into_inner()) -} - -/// Retrieves account resource from the rest client -pub async fn get_account_with_state( - client: &aptos_rest_client::Client, - address: AccountAddress, -) -> CliTypedResult<(Account, State)> { - let account_response = client - .get_account(address) - .await - .map_err(|err| CliError::ApiError(err.to_string()))?; - Ok(account_response.into_parts()) -} - -/// Retrieves sequence number from the rest client -pub async fn get_sequence_number( - client: &aptos_rest_client::Client, - address: AccountAddress, -) -> CliTypedResult { - Ok(get_account(client, address).await?.sequence_number) -} - -/// Retrieves the auth key from the rest client -pub async fn get_auth_key( - client: &aptos_rest_client::Client, - address: AccountAddress, -) -> CliTypedResult { - Ok(get_account(client, address).await?.authentication_key) -} - -/// Retrieves the chain id from the rest client -pub async fn chain_id(rest_client: &Client) -> CliTypedResult { - let state = rest_client - .get_ledger_information() - .await - .map_err(|err| CliError::ApiError(err.to_string()))? - .into_inner(); - Ok(ChainId::new(state.chain_id)) -} - -/// Error message for parsing a map -const PARSE_MAP_SYNTAX_MSG: &str = "Invalid syntax for map. Example: Name=Value,Name2=Value"; - -/// Parses an inline map of values -/// -/// Example: Name=Value,Name2=Value -pub fn parse_map(str: &str) -> anyhow::Result> -where - K::Err: 'static + std::error::Error + Send + Sync, - V::Err: 'static + std::error::Error + Send + Sync, -{ - let mut map = BTreeMap::new(); - - // Split pairs by commas - for pair in str.split_terminator(',') { - // Split pairs by = then trim off any spacing - let (first, second): (&str, &str) = pair - .split_terminator('=') - .collect_tuple() - .ok_or_else(|| anyhow::Error::msg(PARSE_MAP_SYNTAX_MSG))?; - let first = first.trim(); - let second = second.trim(); - if first.is_empty() || second.is_empty() { - return Err(anyhow::Error::msg(PARSE_MAP_SYNTAX_MSG)); - } - - // At this point, we just give error messages appropriate to parsing - let key: K = K::from_str(first)?; - let value: V = V::from_str(second)?; - map.insert(key, value); - } - Ok(map) -} - -/// Generate a vanity account for Ed25519 single signer scheme, either standard or multisig. -/// -/// The default authentication key for an Ed25519 account is the same as the account address. Hence -/// for a standard account, this function generates Ed25519 private keys until finding one that has -/// an authentication key (account address) that begins with the given vanity prefix. -/// -/// For a multisig account, this function generates private keys until finding one that can create -/// a multisig account with the given vanity prefix as its first transaction (sequence number 0). -/// -/// Note that while a valid hex string must have an even number of characters, a vanity prefix can -/// have an odd number of characters since account addresses are human-readable. -/// -/// `vanity_prefix_ref` is a reference to a hex string vanity prefix, optionally prefixed with "0x". -/// For example "0xaceface" or "d00d". -pub fn generate_vanity_account_ed25519( - vanity_prefix_ref: &str, - multisig: bool, -) -> CliTypedResult { - let vanity_prefix_ref = vanity_prefix_ref - .strip_prefix("0x") - .unwrap_or(vanity_prefix_ref); // Optionally strip leading 0x from input string. - let mut to_check_if_is_hex = String::from(vanity_prefix_ref); - // If an odd number of characters append a 0 for verifying that prefix contains valid hex. - if to_check_if_is_hex.len() % 2 != 0 { - to_check_if_is_hex += "0" - }; - hex::decode(to_check_if_is_hex). // Check that the vanity prefix can be decoded into hex. - map_err(|error| CliError::CommandArgumentError(format!( - "The vanity prefix could not be decoded to hex: {}", error)))?; - let mut key_generator = KeyGen::from_os_rng(); // Get random key generator. - loop { - // Generate new keys until finding a match against the vanity prefix. - let private_key = key_generator.generate_ed25519_private_key(); - let mut account_address = - account_address_from_public_key(&Ed25519PublicKey::from(&private_key)); - if multisig { - account_address = create_multisig_account_address(account_address, 0) - }; - if account_address - .short_str_lossless() - .starts_with(vanity_prefix_ref) - { - return Ok(private_key); - }; - } -} - -pub fn current_dir() -> CliTypedResult { - env::current_dir().map_err(|err| { - CliError::UnexpectedError(format!("Failed to get current directory {}", err)) - }) -} - -pub fn dir_default_to_current(maybe_dir: Option) -> CliTypedResult { - if let Some(dir) = maybe_dir { - Ok(dir) - } else { - current_dir() - } -} - -pub fn create_dir_if_not_exist(dir: &Path) -> CliTypedResult<()> { - // Check if the directory exists, if it's not a dir, it will also fail here - if !dir.exists() || !dir.is_dir() { - std::fs::create_dir_all(dir).map_err(|e| CliError::IO(dir.display().to_string(), e))?; - debug!("Created {} folder", dir.display()); - } else { - debug!("{} folder already exists", dir.display()); - } - Ok(()) -} - -/// Reads a line from input -pub fn read_line(input_name: &'static str) -> CliTypedResult { - let mut input_buf = String::new(); - let _ = std::io::stdin() - .read_line(&mut input_buf) - .map_err(|err| CliError::IO(input_name.to_string(), err))?; - - Ok(input_buf) -} - -/// Fund account (and possibly create it) from a faucet -pub async fn fund_account( - faucet_url: Url, - num_octas: u64, - address: AccountAddress, -) -> CliTypedResult> { - let response = reqwest::Client::new() - .post(format!( - "{}v1/mint?amount={}&auth_key={}", - faucet_url, num_octas, address - )) - .body("{}") - .send() - .await - .map_err(|err| { - CliError::ApiError(format!("Failed to fund account with faucet: {:#}", err)) - })?; - if response.status() == 200 { - let hashes: Vec = response - .json() - .await - .map_err(|err| CliError::UnexpectedError(err.to_string()))?; - Ok(hashes) - } else { - Err(CliError::ApiError(format!( - "Faucet issue: {}", - response.status() - ))) - } -} - -/// Fund by public key (and possibly create it) from a faucet -pub async fn fund_pub_key( - faucet_url: Url, - pub_key: String, -) -> CliTypedResult> { - let url = format!( - "{}v1/mint?&pub_key={}", - faucet_url, pub_key - ); - let response = reqwest::Client::new() - .post(url) - .body("{}") - .send() - .await - .map_err(|err| { - CliError::ApiError(format!("Failed to fund account with faucet: {:#}", err)) - })?; - if response.status() == 200 { - let hashes: Vec = response - .json() - .await - .map_err(|err| CliError::UnexpectedError(err.to_string()))?; - Ok(hashes) - } else { - Err(CliError::ApiError(format!( - "Faucet issue: {}", - response.status() - ))) - } -} - -/// Wait for transactions, returning an error if any of them fail. -pub async fn wait_for_transactions( - client: &aptos_rest_client::Client, - hashes: Vec, -) -> CliTypedResult<()> { - let sys_time = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .map_err(|e| CliError::UnexpectedError(e.to_string()))? - .as_secs() - + 30; - for hash in hashes { - client - .wait_for_transaction_by_hash( - hash.into(), - sys_time, - Some(Duration::from_secs(60)), - None, - ) - .await?; - } - Ok(()) -} - -pub fn start_logger() { - let mut logger = aptos_logger::Logger::new(); - logger.channel_size(1000).is_async(false).level(Level::Warn); - logger.build(); -} - -/// For transaction payload and options, either get gas profile or submit for execution. -pub async fn profile_or_submit( - payload: TransactionPayload, - txn_options_ref: &TransactionOptions, -) -> CliTypedResult { - // Profile gas if needed. - if txn_options_ref.profile_gas { - txn_options_ref.profile_gas(payload).await - } else { - // Otherwise submit the transaction. - txn_options_ref - .submit_transaction(payload) - .await - .map(TransactionSummary::from) - } -} diff --git a/m1/m1-cli/src/config/mod.rs b/m1/m1-cli/src/config/mod.rs deleted file mode 100644 index af4da540f..000000000 --- a/m1/m1-cli/src/config/mod.rs +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - types::{ - CliCommand, CliConfig, CliError, CliResult, CliTypedResult, ConfigSearchMode, - ProfileSummary, CONFIG_FOLDER, - }, - utils::{create_dir_if_not_exist, current_dir, read_from_file, write_to_user_only_file}, - }, - genesis::git::{from_yaml, to_yaml}, - Tool, -}; -use async_trait::async_trait; -use clap::{ArgEnum, CommandFactory, Parser}; -use clap_complete::{generate, Shell}; -use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, fmt::Formatter, path::PathBuf, str::FromStr}; - -/// Tool for interacting with configuration of the Movement CLI tool -/// -/// This tool handles the global configuration of the CLI tool for -/// default configuration, and user specific settings. -#[derive(Parser)] -pub enum ConfigTool { - Init(crate::common::init::InitTool), - GenerateShellCompletions(GenerateShellCompletions), - SetGlobalConfig(SetGlobalConfig), - ShowGlobalConfig(ShowGlobalConfig), - ShowProfiles(ShowProfiles), -} - -impl ConfigTool { - pub async fn execute(self) -> CliResult { - match self { - ConfigTool::Init(tool) => tool.execute_serialized_success().await, - ConfigTool::GenerateShellCompletions(tool) => tool.execute_serialized_success().await, - ConfigTool::SetGlobalConfig(tool) => tool.execute_serialized().await, - ConfigTool::ShowGlobalConfig(tool) => tool.execute_serialized().await, - ConfigTool::ShowProfiles(tool) => tool.execute_serialized().await, - } - } -} - -/// Generate shell completion files -/// -/// First generate the completion file, then follow the shell specific directions on how -/// to install the completion file. -#[derive(Parser)] -pub struct GenerateShellCompletions { - /// Shell to generate completions for one of [bash, elvish, powershell, zsh] - #[clap(long)] - shell: Shell, - - /// File to output shell completions to - #[clap(long, parse(from_os_str))] - output_file: PathBuf, -} - -#[async_trait] -impl CliCommand<()> for GenerateShellCompletions { - fn command_name(&self) -> &'static str { - "GenerateShellCompletions" - } - - async fn execute(self) -> CliTypedResult<()> { - let mut command = Tool::command(); - let mut file = std::fs::File::create(self.output_file.as_path()) - .map_err(|err| CliError::IO(self.output_file.display().to_string(), err))?; - generate(self.shell, &mut command, "movement".to_string(), &mut file); - Ok(()) - } -} - -/// Set global configuration settings -/// -/// Any configuration flags that are not provided will not be changed -#[derive(Parser, Debug)] -pub struct SetGlobalConfig { - /// A configuration for where to place and use the config - /// - /// `Workspace` will put the `.aptos/` folder in the current directory, where - /// `Global` will put the `.aptos/` folder in your home directory - #[clap(long)] - config_type: Option, - /// A configuration for how to expect the prompt response - /// - /// Option can be one of ["yes", "no", "prompt"], "yes" runs cli with "--assume-yes", where - /// "no" runs cli with "--assume-no", default: "prompt" - #[clap(long)] - default_prompt_response: Option, -} - -#[async_trait] -impl CliCommand for SetGlobalConfig { - fn command_name(&self) -> &'static str { - "SetGlobalConfig" - } - - async fn execute(self) -> CliTypedResult { - // Load the global config - let mut config = GlobalConfig::load()?; - - // Enable all features that are actually listed - if let Some(config_type) = self.config_type { - config.config_type = Some(config_type); - } - - if let Some(default_prompt_response) = self.default_prompt_response { - config.default_prompt_response = default_prompt_response; - } - - config.save()?; - config.display() - } -} - -/// Shows the current profiles available -/// -/// This will only show public information and will not show -/// private information -#[derive(Parser, Debug)] -pub struct ShowProfiles { - /// Which profile to show - /// - /// If provided, show only this profile - #[clap(long)] - profile: Option, -} - -#[async_trait] -impl CliCommand> for ShowProfiles { - fn command_name(&self) -> &'static str { - "ShowProfiles" - } - - async fn execute(self) -> CliTypedResult> { - // Load the profile config - let config = CliConfig::load(ConfigSearchMode::CurrentDir)?; - Ok(config - .profiles - .unwrap_or_default() - .into_iter() - .filter(|(key, _)| { - if let Some(ref profile) = self.profile { - profile == key - } else { - true - } - }) - .map(|(key, profile)| (key, ProfileSummary::from(&profile))) - .collect()) - } -} - -/// Shows the properties in the global config -#[derive(Parser, Debug)] -pub struct ShowGlobalConfig {} - -#[async_trait] -impl CliCommand for ShowGlobalConfig { - fn command_name(&self) -> &'static str { - "ShowGlobalConfig" - } - - async fn execute(self) -> CliTypedResult { - // Load the global config - let config = GlobalConfig::load()?; - - config.display() - } -} - -const GLOBAL_CONFIG_FILE: &str = "global_config.yaml"; - -/// A global configuration for global settings related to a user -#[derive(Serialize, Deserialize, Debug, Default)] -pub struct GlobalConfig { - /// Whether to be using Global or Workspace mode - #[serde(skip_serializing_if = "Option::is_none")] - pub config_type: Option, - /// Prompt response type - #[serde(default)] - pub default_prompt_response: PromptResponseType, -} - -impl GlobalConfig { - /// Fill in defaults for display via the CLI - pub fn display(mut self) -> CliTypedResult { - if self.config_type.is_none() { - self.config_type = Some(ConfigType::default()); - } - - Ok(self) - } - - pub fn load() -> CliTypedResult { - let path = global_folder()?.join(GLOBAL_CONFIG_FILE); - if path.exists() { - from_yaml(&String::from_utf8(read_from_file(path.as_path())?)?) - } else { - // If we don't have a config, let's load the default - Ok(GlobalConfig::default()) - } - } - - /// Get the config location based on the type - pub fn get_config_location(&self, mode: ConfigSearchMode) -> CliTypedResult { - match self.config_type.unwrap_or_default() { - ConfigType::Global => global_folder(), - ConfigType::Workspace => find_workspace_config(current_dir()?, mode), - } - } - - /// Get the prompt options from global config - pub fn get_default_prompt_response(&self) -> Option { - match self.default_prompt_response { - PromptResponseType::Prompt => None, // prompt - PromptResponseType::Yes => Some(true), // assume_yes - PromptResponseType::No => Some(false), // assume_no - } - } - - fn save(&self) -> CliTypedResult<()> { - let global_folder = global_folder()?; - create_dir_if_not_exist(global_folder.as_path())?; - - write_to_user_only_file( - global_folder.join(GLOBAL_CONFIG_FILE).as_path(), - "Global Config", - &to_yaml(&self)?.into_bytes(), - ) - } -} - -fn global_folder() -> CliTypedResult { - if let Some(dir) = dirs::home_dir() { - Ok(dir.join(CONFIG_FOLDER)) - } else { - Err(CliError::UnexpectedError( - "Unable to retrieve home directory".to_string(), - )) - } -} - -fn find_workspace_config( - starting_path: PathBuf, - mode: ConfigSearchMode, -) -> CliTypedResult { - match mode { - ConfigSearchMode::CurrentDir => Ok(starting_path.join(CONFIG_FOLDER)), - ConfigSearchMode::CurrentDirAndParents => { - let mut current_path = starting_path.clone(); - loop { - current_path.push(CONFIG_FOLDER); - if current_path.is_dir() { - break Ok(current_path); - } else if !(current_path.pop() && current_path.pop()) { - // If we aren't able to find the folder, we'll create a new one right here - break Ok(starting_path.join(CONFIG_FOLDER)); - } - } - }, - } -} - -const GLOBAL: &str = "global"; -const WORKSPACE: &str = "workspace"; - -/// A configuration for where to place and use the config -/// -/// Workspace allows for multiple configs based on location, where -/// Global allows for one config for every part of the code -#[derive(Debug, Copy, Clone, Serialize, Deserialize, ArgEnum)] -pub enum ConfigType { - /// Per system user configuration put in `/.aptos` - Global, - /// Per directory configuration put in `/.aptos` - Workspace, -} - -impl Default for ConfigType { - fn default() -> Self { - // TODO: When we version up, we can change this to global - Self::Workspace - } -} - -impl std::fmt::Display for ConfigType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - ConfigType::Global => GLOBAL, - ConfigType::Workspace => WORKSPACE, - }) - } -} - -impl FromStr for ConfigType { - type Err = CliError; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().trim() { - GLOBAL => Ok(Self::Global), - WORKSPACE => Ok(Self::Workspace), - _ => Err(CliError::CommandArgumentError( - "Invalid config type, must be one of [global, workspace]".to_string(), - )), - } - } -} - -const PROMPT: &str = "prompt"; -const ASSUME_YES: &str = "yes"; -const ASSUME_NO: &str = "no"; - -/// A configuration for how to expect the prompt response -/// -/// Option can be one of ["yes", "no", "prompt"], "yes" runs cli with "--assume-yes", where -/// "no" runs cli with "--assume-no", default: "prompt" -#[derive(Debug, Copy, Clone, Serialize, Deserialize, ArgEnum)] -pub enum PromptResponseType { - /// normal prompt - Prompt, - /// `--assume-yes` - Yes, - /// `--assume-no` - No, -} - -impl Default for PromptResponseType { - fn default() -> Self { - Self::Prompt - } -} - -impl std::fmt::Display for PromptResponseType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - PromptResponseType::Prompt => PROMPT, - PromptResponseType::Yes => ASSUME_YES, - PromptResponseType::No => ASSUME_NO, - }) - } -} - -impl FromStr for PromptResponseType { - type Err = CliError; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().trim() { - PROMPT => Ok(Self::Prompt), - ASSUME_YES => Ok(Self::Yes), - ASSUME_NO => Ok(Self::No), - _ => Err(CliError::CommandArgumentError( - "Invalid prompt response type, must be one of [yes, no, prompt]".to_string(), - )), - } - } -} diff --git a/m1/m1-cli/src/faucet/mod.rs b/m1/m1-cli/src/faucet/mod.rs deleted file mode 100644 index 4b4a6d91b..000000000 --- a/m1/m1-cli/src/faucet/mod.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::{ - types::{CliCommand, CliTypedResult} -}; -use async_trait::async_trait; -use crate::common::types::{FaucetOptions, ProfileOptions, RestOptions}; -use crate::common::utils::{fund_pub_key, wait_for_transactions}; -use clap::Parser; - -#[derive(Debug, Parser)] -pub struct FaucetTool { - #[clap(long)] - pub_key: Option, - #[clap(flatten)] - pub(crate) faucet_options: FaucetOptions, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, -} - -impl FaucetTool { - fn pub_key(&self, profile: &ProfileOptions) -> CliTypedResult { - match &self.pub_key { - Some(pub_key) => Ok(pub_key.clone()), - None => Ok((profile.public_key()?).to_string()), - } - } -} - -#[async_trait] -impl CliCommand for FaucetTool { - fn command_name(&self) -> &'static str { - "Faucet" - } - - async fn execute(self) -> CliTypedResult { - let profile = &self.profile_options; - let hashes = fund_pub_key( - self.faucet_options.faucet_url(&profile)?, - self.pub_key(&profile)?, - ).await?; - let client = self.rest_options.client_raw(self.faucet_options.faucet_url(&profile)?)?; - wait_for_transactions(&client, hashes).await?; - return Ok(format!( - "Added 10 MOV to account {}", self.pub_key(&profile)? - )); - } -} - diff --git a/m1/m1-cli/src/ffi.rs b/m1/m1-cli/src/ffi.rs deleted file mode 100644 index af9ba937a..000000000 --- a/m1/m1-cli/src/ffi.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -#![allow(unsafe_code)] - -use crate::Tool; -use clap::Parser; -use std::{ - ffi::{c_char, CStr, CString}, - thread, -}; -use tokio::runtime::Runtime; - -/// # Safety -/// -/// Run the movement CLI synchronously -/// Note: This function should only be called from other SDK (i.g Typescript) -/// -/// Return: the pointer to CLIResult c string -#[no_mangle] -pub unsafe extern "C" fn run_aptos_sync(s: *const c_char) -> *const c_char { - let c_str = unsafe { - assert!(!s.is_null()); - CStr::from_ptr(s) - }; - - // split string by spaces - let input_string = c_str.to_str().unwrap().split_whitespace(); - - // Create a new Tokio runtime and block on the execution of `cli.execute()` - let result_string = Runtime::new().unwrap().block_on(async move { - let cli = Tool::parse_from(input_string); - let result = cli.execute().await; - result - }); - - let res_cstr = CString::new(result_string.unwrap()).unwrap(); - - // Return a pointer to the C string - res_cstr.into_raw() -} - -/// # Safety -/// -/// Run the movement CLI async; Use this function if you are expecting the movement CLI command -/// to run in the background, or different thread -/// Note: This function should only be called from other SDK (i.g Typescript) -/// -/// Return: the pointer to c string: 'true' -#[no_mangle] -pub unsafe extern "C" fn run_aptos_async(s: *mut c_char) -> *mut c_char { - println!("Running movement..."); - let c_str = unsafe { - assert!(!s.is_null()); - CStr::from_ptr(s) - }; - - // Spawn a new thread to run the CLI - thread::spawn(move || { - let rt = Runtime::new().unwrap(); - let input_string = c_str.to_str().unwrap().split_whitespace(); - let cli = Tool::parse_from(input_string); - - // Run the CLI once - rt.block_on(async { cli.execute().await }) - .expect("Failed to run CLI"); - }); - - // Return pointer - CString::new("true").unwrap().into_raw() -} - -/// # Safety -/// -/// After running the movement CLI using FFI. Make sure to invoke this method to free up or -/// deallocate the memory -#[no_mangle] -pub unsafe extern "C" fn free_cstring(s: *mut c_char) { - unsafe { - if s.is_null() { - return; - } - CString::from_raw(s) - }; -} diff --git a/m1/m1-cli/src/genesis/git.rs b/m1/m1-cli/src/genesis/git.rs deleted file mode 100644 index 383689151..000000000 --- a/m1/m1-cli/src/genesis/git.rs +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - types::{CliError, CliTypedResult}, - utils::{create_dir_if_not_exist, write_to_file}, - }, - CliCommand, -}; -use aptos_config::config::Token; -use aptos_framework::ReleaseBundle; -use aptos_genesis::config::Layout; -use aptos_github_client::Client as GithubClient; -use async_trait::async_trait; -use clap::Parser; -use serde::{de::DeserializeOwned, Serialize}; -use std::{ - fmt::Debug, - io::Read, - path::{Path, PathBuf}, - str::FromStr, -}; - -pub const LAYOUT_FILE: &str = "layout.yaml"; -pub const OPERATOR_FILE: &str = "operator.yaml"; -pub const OWNER_FILE: &str = "owner.yaml"; -pub const FRAMEWORK_NAME: &str = "framework.mrb"; -pub const BALANCES_FILE: &str = "balances.yaml"; -pub const EMPLOYEE_VESTING_ACCOUNTS_FILE: &str = "employee_vesting_accounts.yaml"; - -/// Setup a shared Git repository for Genesis -/// -/// This will setup a folder or an online Github repository to be used -/// for Genesis. If it's the local, it will create the folders but not -/// set up a Git repository. -#[derive(Parser)] -pub struct SetupGit { - #[clap(flatten)] - pub(crate) git_options: GitOptions, - - /// Path to the `Layout` file which defines where all the files are - #[clap(long, parse(from_os_str))] - pub(crate) layout_file: PathBuf, -} - -#[async_trait] -impl CliCommand<()> for SetupGit { - fn command_name(&self) -> &'static str { - "SetupGit" - } - - async fn execute(self) -> CliTypedResult<()> { - let layout = Layout::from_disk(&self.layout_file)?; - - // Upload layout file to ensure we can read later - let client = self.git_options.get_client()?; - client.put(Path::new(LAYOUT_FILE), &layout)?; - - Ok(()) - } -} - -#[derive(Clone, Debug, Default)] -pub struct GithubRepo { - owner: String, - repository: String, -} - -impl FromStr for GithubRepo { - type Err = CliError; - - fn from_str(s: &str) -> Result { - let parts: Vec<_> = s.split('/').collect(); - if parts.len() != 2 { - Err(CliError::CommandArgumentError("Invalid repository must be of the form 'owner/repository` e.g. 'aptos-labs/aptos-core'".to_string())) - } else { - Ok(GithubRepo { - owner: parts.first().unwrap().to_string(), - repository: parts.get(1).unwrap().to_string(), - }) - } - } -} - -#[derive(Clone, Default, Parser)] -pub struct GitOptions { - /// Github repository e.g. 'aptos-labs/aptos-core' - /// - /// Mutually exclusive with `--local-repository-dir` - #[clap(long)] - pub(crate) github_repository: Option, - - /// Github repository branch e.g. main - #[clap(long, default_value = "main")] - pub(crate) github_branch: String, - - /// Path to Github API token. Token must have repo:* permissions - #[clap(long, parse(from_os_str))] - pub(crate) github_token_file: Option, - - /// Path to local git repository - /// - /// Mutually exclusive with `--github-repository` - #[clap(long, parse(from_os_str))] - pub(crate) local_repository_dir: Option, -} - -impl GitOptions { - pub fn get_client(self) -> CliTypedResult { - if self.github_repository.is_none() - && self.github_token_file.is_none() - && self.local_repository_dir.is_some() - { - Ok(Client::local(self.local_repository_dir.unwrap())) - } else if self.github_repository.is_some() - && self.github_token_file.is_some() - && self.local_repository_dir.is_none() - { - Client::github( - self.github_repository.unwrap(), - self.github_branch, - self.github_token_file.unwrap(), - ) - } else { - Err(CliError::CommandArgumentError("Must provide either only --local-repository-dir or both --github-repository and --github-token-path".to_string())) - } - } -} - -/// A client for abstracting away local vs Github storage -/// -/// Note: Writes do not commit locally -pub enum Client { - Local(PathBuf), - Github(GithubClient), -} - -impl Client { - pub fn local(path: PathBuf) -> Client { - Client::Local(path) - } - - pub fn github( - repository: GithubRepo, - branch: String, - token_path: PathBuf, - ) -> CliTypedResult { - let token = Token::FromDisk(token_path).read_token()?; - Ok(Client::Github(GithubClient::new( - repository.owner, - repository.repository, - branch, - token, - ))) - } - - /// Retrieves an object as a YAML encoded file from the appropriate storage - pub fn get(&self, path: &Path) -> CliTypedResult { - match self { - Client::Local(local_repository_path) => { - let path = local_repository_path.join(path); - - if !path.exists() { - return Err(CliError::UnableToReadFile( - path.display().to_string(), - "File not found".to_string(), - )); - } - - eprintln!("Reading {}", path.display()); - let mut file = std::fs::File::open(path.as_path()) - .map_err(|e| CliError::IO(path.display().to_string(), e))?; - - let mut contents = String::new(); - file.read_to_string(&mut contents) - .map_err(|e| CliError::IO(path.display().to_string(), e))?; - from_yaml(&contents) - }, - Client::Github(client) => { - from_base64_encoded_yaml(&client.get_file(&path.display().to_string())?) - }, - } - } - - /// Puts an object as a YAML encoded file to the appropriate storage - pub fn put(&self, name: &Path, input: &T) -> CliTypedResult<()> { - match self { - Client::Local(local_repository_path) => { - let path = local_repository_path.join(name); - - // Create repository path and any sub-directories - if let Some(dir) = path.parent() { - self.create_dir(dir)?; - } else { - return Err(CliError::UnexpectedError(format!( - "Path should always have a parent {}", - path.display() - ))); - } - write_to_file( - path.as_path(), - &path.display().to_string(), - to_yaml(input)?.as_bytes(), - )?; - }, - Client::Github(client) => { - client.put(&name.display().to_string(), &to_base64_encoded_yaml(input)?)?; - }, - } - - Ok(()) - } - - pub fn create_dir(&self, dir: &Path) -> CliTypedResult<()> { - match self { - Client::Local(local_repository_path) => { - let path = local_repository_path.join(dir); - create_dir_if_not_exist(path.as_path())?; - }, - Client::Github(_) => { - // There's no such thing as an empty directory in Git, so do nothing - }, - } - - Ok(()) - } - - /// Retrieve framework release bundle. - pub fn get_framework(&self) -> CliTypedResult { - match self { - Client::Local(local_repository_path) => { - let path = local_repository_path.join(FRAMEWORK_NAME); - if !path.exists() { - return Err(CliError::UnableToReadFile( - path.display().to_string(), - "File not found".to_string(), - )); - } - Ok(ReleaseBundle::read(path)?) - }, - Client::Github(client) => { - let bytes = base64::decode(client.get_file(FRAMEWORK_NAME)?)?; - Ok(bcs::from_bytes::(&bytes)?) - }, - } - } -} - -pub fn to_yaml(input: &T) -> CliTypedResult { - Ok(serde_yaml::to_string(input)?) -} - -pub fn from_yaml(input: &str) -> CliTypedResult { - Ok(serde_yaml::from_str(input)?) -} - -pub fn to_base64_encoded_yaml(input: &T) -> CliTypedResult { - Ok(base64::encode(to_yaml(input)?)) -} - -pub fn from_base64_encoded_yaml(input: &str) -> CliTypedResult { - from_yaml(&String::from_utf8(base64::decode(input)?)?) -} diff --git a/m1/m1-cli/src/genesis/keys.rs b/m1/m1-cli/src/genesis/keys.rs deleted file mode 100644 index c283ad7f4..000000000 --- a/m1/m1-cli/src/genesis/keys.rs +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - types::{CliError, CliTypedResult, OptionalPoolAddressArgs, PromptOptions, RngArgs}, - utils::{ - check_if_file_exists, create_dir_if_not_exist, current_dir, dir_default_to_current, - read_from_file, write_to_user_only_file, - }, - }, - genesis::git::{from_yaml, to_yaml, GitOptions, LAYOUT_FILE, OPERATOR_FILE, OWNER_FILE}, - governance::CompileScriptFunction, - CliCommand, -}; -use aptos_genesis::{ - config::{HostAndPort, Layout, OperatorConfiguration, OwnerConfiguration}, - keys::{generate_key_objects, PublicIdentity}, -}; -use aptos_types::{ - account_address::AccountAddress, - transaction::{Script, Transaction, WriteSetPayload}, -}; -use async_trait::async_trait; -use clap::Parser; -use std::path::{Path, PathBuf}; - -const PRIVATE_KEYS_FILE: &str = "private-keys.yaml"; -pub const PUBLIC_KEYS_FILE: &str = "public-keys.yaml"; -const VALIDATOR_FILE: &str = "validator-identity.yaml"; -const VFN_FILE: &str = "validator-full-node-identity.yaml"; - -/// Generate keys for a new validator -/// -/// Generates account key, consensus key, and network key for a validator -/// These keys are used for running a validator or operator in a network -#[derive(Parser)] -pub struct GenerateKeys { - /// Output directory for the key files - #[clap(long, parse(from_os_str))] - pub(crate) output_dir: Option, - - #[clap(flatten)] - pub(crate) pool_address_args: OptionalPoolAddressArgs, - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, - #[clap(flatten)] - pub rng_args: RngArgs, -} - -#[async_trait] -impl CliCommand> for GenerateKeys { - fn command_name(&self) -> &'static str { - "GenerateKeys" - } - - async fn execute(self) -> CliTypedResult> { - let output_dir = dir_default_to_current(self.output_dir.clone())?; - - let private_keys_file = output_dir.join(PRIVATE_KEYS_FILE); - let public_keys_file = output_dir.join(PUBLIC_KEYS_FILE); - let validator_file = output_dir.join(VALIDATOR_FILE); - let vfn_file = output_dir.join(VFN_FILE); - check_if_file_exists(private_keys_file.as_path(), self.prompt_options)?; - check_if_file_exists(public_keys_file.as_path(), self.prompt_options)?; - check_if_file_exists(validator_file.as_path(), self.prompt_options)?; - check_if_file_exists(vfn_file.as_path(), self.prompt_options)?; - - let mut key_generator = self.rng_args.key_generator()?; - let (mut validator_blob, mut vfn_blob, private_identity, public_identity) = - generate_key_objects(&mut key_generator)?; - - // Allow for the owner to be different than the operator - if let Some(pool_address) = self.pool_address_args.pool_address { - validator_blob.account_address = Some(pool_address); - vfn_blob.account_address = Some(pool_address); - } - - // Create the directory if it doesn't exist - create_dir_if_not_exist(output_dir.as_path())?; - - write_to_user_only_file( - private_keys_file.as_path(), - PRIVATE_KEYS_FILE, - to_yaml(&private_identity)?.as_bytes(), - )?; - write_to_user_only_file( - public_keys_file.as_path(), - PUBLIC_KEYS_FILE, - to_yaml(&public_identity)?.as_bytes(), - )?; - write_to_user_only_file( - validator_file.as_path(), - VALIDATOR_FILE, - to_yaml(&validator_blob)?.as_bytes(), - )?; - write_to_user_only_file(vfn_file.as_path(), VFN_FILE, to_yaml(&vfn_blob)?.as_bytes())?; - Ok(vec![ - public_keys_file, - private_keys_file, - validator_file, - vfn_file, - ]) - } -} - -/// Set validator configuration for a single validator -/// -/// This will set the validator configuration for a single validator in the git repository. -/// It will have to be run for each validator expected at genesis. -#[derive(Parser)] -pub struct SetValidatorConfiguration { - /// Name of the validator - #[clap(long)] - pub(crate) username: String, - - /// Host and port pair for the validator e.g. 127.0.0.1:6180 or aptoslabs.com:6180 - #[clap(long)] - pub(crate) validator_host: HostAndPort, - - /// Host and port pair for the fullnode e.g. 127.0.0.1:6180 or aptoslabs.com:6180 - #[clap(long)] - pub(crate) full_node_host: Option, - - /// Stake amount for stake distribution - #[clap(long, default_value_t = 1)] - pub(crate) stake_amount: u64, - - /// Commission rate to pay operator - /// - /// This is a percentage between 0% and 100% - #[clap(long, default_value_t = 0)] - pub(crate) commission_percentage: u64, - - /// Whether the validator will be joining the genesis validator set - /// - /// If set this validator will already be in the validator set at genesis - #[clap(long)] - pub(crate) join_during_genesis: bool, - - /// Path to private identity generated from GenerateKeys - #[clap(long, parse(from_os_str))] - pub(crate) owner_public_identity_file: Option, - - /// Path to operator public identity, defaults to owner identity - #[clap(long, parse(from_os_str))] - pub(crate) operator_public_identity_file: Option, - - /// Path to voter public identity, defaults to owner identity - #[clap(long, parse(from_os_str))] - pub(crate) voter_public_identity_file: Option, - - #[clap(flatten)] - pub(crate) git_options: GitOptions, -} - -#[async_trait] -impl CliCommand<()> for SetValidatorConfiguration { - fn command_name(&self) -> &'static str { - "SetValidatorConfiguration" - } - - async fn execute(self) -> CliTypedResult<()> { - // Load owner - let owner_keys_file = if let Some(owner_keys_file) = self.owner_public_identity_file { - owner_keys_file - } else { - current_dir()?.join(PUBLIC_KEYS_FILE) - }; - let owner_identity = read_public_identity_file(owner_keys_file.as_path())?; - - // Load voter - let voter_identity = if let Some(voter_keys_file) = self.voter_public_identity_file { - read_public_identity_file(voter_keys_file.as_path())? - } else { - owner_identity.clone() - }; - - // Load operator - let (operator_identity, operator_keys_file) = - if let Some(operator_keys_file) = self.operator_public_identity_file { - ( - read_public_identity_file(operator_keys_file.as_path())?, - operator_keys_file, - ) - } else { - (owner_identity.clone(), owner_keys_file) - }; - - // Extract the possible optional fields - let consensus_public_key = - if let Some(consensus_public_key) = operator_identity.consensus_public_key { - consensus_public_key - } else { - return Err(CliError::CommandArgumentError(format!( - "Failed to read consensus public key from public identity file {}", - operator_keys_file.display() - ))); - }; - - let validator_network_public_key = if let Some(validator_network_public_key) = - operator_identity.validator_network_public_key - { - validator_network_public_key - } else { - return Err(CliError::CommandArgumentError(format!( - "Failed to read validator network public key from public identity file {}", - operator_keys_file.display() - ))); - }; - - let consensus_proof_of_possession = if let Some(consensus_proof_of_possession) = - operator_identity.consensus_proof_of_possession - { - consensus_proof_of_possession - } else { - return Err(CliError::CommandArgumentError(format!( - "Failed to read consensus proof of possession from public identity file {}", - operator_keys_file.display() - ))); - }; - - // Only add the public key if there is a full node - let full_node_network_public_key = if self.full_node_host.is_some() { - operator_identity.full_node_network_public_key - } else { - None - }; - - // Build operator configuration file - let operator_config = OperatorConfiguration { - operator_account_address: operator_identity.account_address.into(), - operator_account_public_key: operator_identity.account_public_key.clone(), - consensus_public_key, - consensus_proof_of_possession, - validator_network_public_key, - validator_host: self.validator_host, - full_node_network_public_key, - full_node_host: self.full_node_host, - }; - - let owner_config = OwnerConfiguration { - owner_account_address: owner_identity.account_address.into(), - owner_account_public_key: owner_identity.account_public_key, - voter_account_address: voter_identity.account_address.into(), - voter_account_public_key: voter_identity.account_public_key, - operator_account_address: operator_identity.account_address.into(), - operator_account_public_key: operator_identity.account_public_key, - stake_amount: self.stake_amount, - commission_percentage: self.commission_percentage, - join_during_genesis: self.join_during_genesis, - }; - - let directory = PathBuf::from(&self.username); - let operator_file = directory.join(OPERATOR_FILE); - let owner_file = directory.join(OWNER_FILE); - - let git_client = self.git_options.get_client()?; - git_client.put(operator_file.as_path(), &operator_config)?; - git_client.put(owner_file.as_path(), &owner_config) - } -} - -pub fn read_public_identity_file(public_identity_file: &Path) -> CliTypedResult { - let bytes = read_from_file(public_identity_file)?; - from_yaml(&String::from_utf8(bytes).map_err(CliError::from)?) -} - -/// Generate a Layout template file -/// -/// This will generate a layout template file for genesis with some default values. To start a -/// new chain, these defaults should be carefully thought through and chosen. -#[derive(Parser)] -pub struct GenerateLayoutTemplate { - /// Path of the output layout template - #[clap(long, parse(from_os_str), default_value = LAYOUT_FILE)] - pub(crate) output_file: PathBuf, - - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, -} - -#[async_trait] -impl CliCommand<()> for GenerateLayoutTemplate { - fn command_name(&self) -> &'static str { - "GenerateLayoutTemplate" - } - - async fn execute(self) -> CliTypedResult<()> { - check_if_file_exists(self.output_file.as_path(), self.prompt_options)?; - let layout = Layout::default(); - - write_to_user_only_file( - self.output_file.as_path(), - &self.output_file.display().to_string(), - to_yaml(&layout)?.as_bytes(), - ) - } -} - -/// Generate a WriteSet genesis -/// -/// This will compile a Move script and generate a writeset from that script. -#[derive(Parser)] -pub struct GenerateAdminWriteSet { - /// Path of the output genesis file - #[clap(long, parse(from_os_str))] - pub(crate) output_file: PathBuf, - - /// Address of the account which execute this script. - #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] - pub(crate) execute_as: AccountAddress, - - #[clap(flatten)] - pub(crate) compile_proposal_args: CompileScriptFunction, - - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, -} - -#[async_trait] -impl CliCommand<()> for GenerateAdminWriteSet { - fn command_name(&self) -> &'static str { - "GenerateAdminWriteSet" - } - - async fn execute(self) -> CliTypedResult<()> { - check_if_file_exists(self.output_file.as_path(), self.prompt_options)?; - let (bytecode, _script_hash) = self - .compile_proposal_args - .compile("GenerateAdminWriteSet", self.prompt_options)?; - - let txn = Transaction::GenesisTransaction(WriteSetPayload::Script { - execute_as: self.execute_as, - script: Script::new(bytecode, vec![], vec![]), - }); - - write_to_user_only_file( - self.output_file.as_path(), - &self.output_file.display().to_string(), - &bcs::to_bytes(&txn).map_err(CliError::from)?, - ) - } -} diff --git a/m1/m1-cli/src/genesis/mod.rs b/m1/m1-cli/src/genesis/mod.rs deleted file mode 100644 index 66f1a5b6c..000000000 --- a/m1/m1-cli/src/genesis/mod.rs +++ /dev/null @@ -1,926 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod git; -pub mod keys; -#[cfg(test)] -mod tests; -pub mod tools; - -use crate::{ - common::{ - types::{CliError, CliTypedResult, PromptOptions}, - utils::{check_if_file_exists, dir_default_to_current, write_to_file}, - }, - genesis::git::{ - Client, GitOptions, BALANCES_FILE, EMPLOYEE_VESTING_ACCOUNTS_FILE, LAYOUT_FILE, - OPERATOR_FILE, OWNER_FILE, - }, - CliCommand, CliResult, -}; -use aptos_crypto::{ - bls12381, ed25519::ED25519_PUBLIC_KEY_LENGTH, x25519, ValidCryptoMaterial, - ValidCryptoMaterialStringExt, -}; -use aptos_genesis::{ - builder::GenesisConfiguration, - config::{ - AccountBalanceMap, EmployeePoolMap, HostAndPort, Layout, StringOperatorConfiguration, - StringOwnerConfiguration, ValidatorConfiguration, - }, - mainnet::MainnetGenesisInfo, - GenesisInfo, -}; -use aptos_logger::info; -use aptos_types::{ - account_address::{AccountAddress, AccountAddressWithChecks}, - on_chain_config::{OnChainConsensusConfig, OnChainExecutionConfig}, -}; -use aptos_vm_genesis::{default_gas_schedule, AccountBalance, EmployeePool}; -use async_trait::async_trait; -use clap::Parser; -use std::{ - cmp::Ordering, - collections::{BTreeMap, BTreeSet, HashSet}, - path::{Path, PathBuf}, - str::FromStr, -}; - -const WAYPOINT_FILE: &str = "waypoint.txt"; -const GENESIS_FILE: &str = "genesis.blob"; - -/// Tool for setting up an Movement chain Genesis transaction -/// -/// This tool sets up a space for multiple initial "validator" -/// accounts to build a genesis transaction for a new chain. -#[derive(Parser)] -pub enum GenesisTool { - GenerateAdminWriteSet(keys::GenerateAdminWriteSet), - GenerateGenesis(GenerateGenesis), - GetPoolAddresses(tools::PoolAddresses), - GenerateKeys(keys::GenerateKeys), - GenerateLayoutTemplate(keys::GenerateLayoutTemplate), - SetupGit(git::SetupGit), - SetValidatorConfiguration(keys::SetValidatorConfiguration), -} - -impl GenesisTool { - pub async fn execute(self) -> CliResult { - match self { - GenesisTool::GenerateAdminWriteSet(tool) => tool.execute_serialized_success().await, - GenesisTool::GenerateGenesis(tool) => tool.execute_serialized().await, - GenesisTool::GetPoolAddresses(tool) => tool.execute_serialized().await, - GenesisTool::GenerateKeys(tool) => tool.execute_serialized().await, - GenesisTool::GenerateLayoutTemplate(tool) => tool.execute_serialized_success().await, - GenesisTool::SetupGit(tool) => tool.execute_serialized_success().await, - GenesisTool::SetValidatorConfiguration(tool) => tool.execute_serialized_success().await, - } - } -} - -/// Generate genesis from a git repository -/// -/// This will create a genesis.blob and a waypoint.txt to be used for -/// running a network -#[derive(Parser)] -pub struct GenerateGenesis { - /// Output directory for Genesis file and waypoint - #[clap(long, parse(from_os_str))] - output_dir: Option, - /// Whether this is mainnet genesis. - /// - /// Default is false - #[clap(long)] - mainnet: bool, - - #[clap(flatten)] - prompt_options: PromptOptions, - #[clap(flatten)] - git_options: GitOptions, -} - -#[async_trait] -impl CliCommand> for GenerateGenesis { - fn command_name(&self) -> &'static str { - "GenerateGenesis" - } - - async fn execute(self) -> CliTypedResult> { - let output_dir = dir_default_to_current(self.output_dir.clone())?; - let genesis_file = output_dir.join(GENESIS_FILE); - let waypoint_file = output_dir.join(WAYPOINT_FILE); - check_if_file_exists(genesis_file.as_path(), self.prompt_options)?; - check_if_file_exists(waypoint_file.as_path(), self.prompt_options)?; - - // Generate genesis and waypoint files - let (genesis_bytes, waypoint) = if self.mainnet { - let mut mainnet_genesis = fetch_mainnet_genesis_info(self.git_options)?; - let genesis_bytes = bcs::to_bytes(mainnet_genesis.clone().get_genesis()) - .map_err(|e| CliError::BCS(GENESIS_FILE, e))?; - (genesis_bytes, mainnet_genesis.generate_waypoint()?) - } else { - let mut test_genesis = fetch_genesis_info(self.git_options)?; - let genesis_bytes = bcs::to_bytes(test_genesis.clone().get_genesis()) - .map_err(|e| CliError::BCS(GENESIS_FILE, e))?; - (genesis_bytes, test_genesis.generate_waypoint()?) - }; - write_to_file(genesis_file.as_path(), GENESIS_FILE, &genesis_bytes)?; - write_to_file( - waypoint_file.as_path(), - WAYPOINT_FILE, - waypoint.to_string().as_bytes(), - )?; - Ok(vec![genesis_file, waypoint_file]) - } -} - -/// Retrieves all information for mainnet genesis from the Git repository -pub fn fetch_mainnet_genesis_info(git_options: GitOptions) -> CliTypedResult { - let client = git_options.get_client()?; - let layout: Layout = client.get(Path::new(LAYOUT_FILE))?; - - if layout.root_key.is_some() { - return Err(CliError::UnexpectedError( - "Root key must not be set for mainnet.".to_string(), - )); - } - - let total_supply = layout.total_supply.ok_or_else(|| { - CliError::UnexpectedError("Layout file does not have `total_supply`".to_string()) - })?; - - let account_balance_map: AccountBalanceMap = client.get(Path::new(BALANCES_FILE))?; - let accounts: Vec = account_balance_map.try_into()?; - - // Check that the supply matches the total - let total_balance_supply: u64 = accounts.iter().map(|inner| inner.balance).sum(); - if total_supply != total_balance_supply { - return Err(CliError::UnexpectedError(format!( - "Total supply seen {} doesn't match expected total supply {}", - total_balance_supply, total_supply - ))); - } - - // Check that the user has a reasonable amount of APT, since below the minimum gas amount is - // not useful 1 APT minimally - const MIN_USEFUL_AMOUNT: u64 = 200000000; - let ten_percent_of_total = total_supply / 10; - for account in accounts.iter() { - if account.balance != 0 && account.balance < MIN_USEFUL_AMOUNT { - return Err(CliError::UnexpectedError(format!( - "Account {} has an initial supply below expected amount {} < {}", - account.account_address, account.balance, MIN_USEFUL_AMOUNT - ))); - } else if account.balance > ten_percent_of_total { - return Err(CliError::UnexpectedError(format!( - "Account {} has an more than 10% of the total balance {} > {}", - account.account_address, account.balance, ten_percent_of_total - ))); - } - } - - // Keep track of accounts for later lookup of balances - let initialized_accounts: BTreeMap = accounts - .iter() - .map(|inner| (inner.account_address, inner.balance)) - .collect(); - - let employee_vesting_accounts: EmployeePoolMap = - client.get(Path::new(EMPLOYEE_VESTING_ACCOUNTS_FILE))?; - - let employee_validators: Vec<_> = employee_vesting_accounts - .inner - .iter() - .map(|inner| inner.validator.clone()) - .collect(); - let employee_vesting_accounts: Vec = employee_vesting_accounts.try_into()?; - let validators = get_validator_configs(&client, &layout, true).map_err(parse_error)?; - let mut unique_accounts = BTreeSet::new(); - let mut unique_network_keys = HashSet::new(); - let mut unique_consensus_keys = HashSet::new(); - let mut unique_consensus_pop = HashSet::new(); - let mut unique_hosts = HashSet::new(); - - validate_employee_accounts( - &employee_vesting_accounts, - &initialized_accounts, - &mut unique_accounts, - )?; - - let mut seen_owners = BTreeMap::new(); - validate_validators( - &layout, - &employee_validators, - &initialized_accounts, - &mut unique_accounts, - &mut unique_network_keys, - &mut unique_consensus_keys, - &mut unique_consensus_pop, - &mut unique_hosts, - &mut seen_owners, - true, - )?; - validate_validators( - &layout, - &validators, - &initialized_accounts, - &mut unique_accounts, - &mut unique_network_keys, - &mut unique_consensus_keys, - &mut unique_consensus_pop, - &mut unique_hosts, - &mut seen_owners, - false, - )?; - - let framework = client.get_framework()?; - Ok(MainnetGenesisInfo::new( - layout.chain_id, - accounts, - employee_vesting_accounts, - validators, - framework, - &GenesisConfiguration { - allow_new_validators: true, - epoch_duration_secs: layout.epoch_duration_secs, - is_test: false, - min_stake: layout.min_stake, - min_voting_threshold: layout.min_voting_threshold, - max_stake: layout.max_stake, - recurring_lockup_duration_secs: layout.recurring_lockup_duration_secs, - required_proposer_stake: layout.required_proposer_stake, - rewards_apy_percentage: layout.rewards_apy_percentage, - voting_duration_secs: layout.voting_duration_secs, - voting_power_increase_limit: layout.voting_power_increase_limit, - employee_vesting_start: layout.employee_vesting_start, - employee_vesting_period_duration: layout.employee_vesting_period_duration, - consensus_config: OnChainConsensusConfig::default(), - execution_config: OnChainExecutionConfig::default(), - gas_schedule: default_gas_schedule(), - }, - )?) -} - -/// Retrieves all information for genesis from the Git repository -pub fn fetch_genesis_info(git_options: GitOptions) -> CliTypedResult { - let client = git_options.get_client()?; - let layout: Layout = client.get(Path::new(LAYOUT_FILE))?; - - if layout.root_key.is_none() { - return Err(CliError::UnexpectedError( - "Layout field root_key was not set. Please provide a hex encoded Ed25519PublicKey." - .to_string(), - )); - } - - let validators = get_validator_configs(&client, &layout, false).map_err(parse_error)?; - let framework = client.get_framework()?; - Ok(GenesisInfo::new( - layout.chain_id, - layout.root_key.unwrap(), - validators, - framework, - &GenesisConfiguration { - allow_new_validators: layout.allow_new_validators, - epoch_duration_secs: layout.epoch_duration_secs, - is_test: layout.is_test, - min_stake: layout.min_stake, - min_voting_threshold: layout.min_voting_threshold, - max_stake: layout.max_stake, - recurring_lockup_duration_secs: layout.recurring_lockup_duration_secs, - required_proposer_stake: layout.required_proposer_stake, - rewards_apy_percentage: layout.rewards_apy_percentage, - voting_duration_secs: layout.voting_duration_secs, - voting_power_increase_limit: layout.voting_power_increase_limit, - employee_vesting_start: layout.employee_vesting_start, - employee_vesting_period_duration: layout.employee_vesting_period_duration, - consensus_config: OnChainConsensusConfig::default(), - execution_config: OnChainExecutionConfig::default(), - gas_schedule: default_gas_schedule(), - }, - )?) -} - -fn parse_error(errors: Vec) -> CliError { - eprintln!( - "Failed to parse genesis inputs:\n{}", - serde_yaml::to_string(&errors).unwrap() - ); - CliError::UnexpectedError("Failed to parse genesis inputs".to_string()) -} - -fn get_validator_configs( - client: &Client, - layout: &Layout, - is_mainnet: bool, -) -> Result, Vec> { - let mut validators = Vec::new(); - let mut errors = Vec::new(); - for user in &layout.users { - match get_config(client, user, is_mainnet) { - Ok(validator) => { - validators.push(validator); - }, - Err(failure) => { - if let CliError::UnexpectedError(failure) = failure { - errors.push(format!("{}: {}", user, failure)); - } else { - errors.push(format!("{}: {:?}", user, failure)); - } - }, - } - } - - if errors.is_empty() { - Ok(validators) - } else { - Err(errors) - } -} - -/// Do proper parsing so more information is known about failures -fn get_config( - client: &Client, - user: &str, - is_mainnet: bool, -) -> CliTypedResult { - // Load a user's configuration files - let dir = PathBuf::from(user); - let owner_file = dir.join(OWNER_FILE); - let owner_file = owner_file.as_path(); - let owner_config = client.get::(owner_file)?; - - // Check and convert fields in owner file - let owner_account_address: AccountAddress = parse_required_option( - &owner_config.owner_account_address, - owner_file, - "owner_account_address", - AccountAddressWithChecks::from_str, - )? - .into(); - let owner_account_public_key = parse_required_option( - &owner_config.owner_account_public_key, - owner_file, - "owner_account_public_key", - |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), - )?; - - let operator_account_address: AccountAddress = parse_required_option( - &owner_config.operator_account_address, - owner_file, - "operator_account_address", - AccountAddressWithChecks::from_str, - )? - .into(); - let operator_account_public_key = parse_required_option( - &owner_config.operator_account_public_key, - owner_file, - "operator_account_public_key", - |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), - )?; - - let voter_account_address: AccountAddress = parse_required_option( - &owner_config.voter_account_address, - owner_file, - "voter_account_address", - AccountAddressWithChecks::from_str, - )? - .into(); - let voter_account_public_key = parse_required_option( - &owner_config.voter_account_public_key, - owner_file, - "voter_account_public_key", - |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), - )?; - - let stake_amount = parse_required_option( - &owner_config.stake_amount, - owner_file, - "stake_amount", - u64::from_str, - )?; - - // Default to 0 for commission percentage if missing. - let commission_percentage = parse_optional_option( - &owner_config.commission_percentage, - owner_file, - "commission_percentage", - u64::from_str, - )? - .unwrap_or(0); - - // Default to true for whether the validator should be joining during genesis. - let join_during_genesis = parse_optional_option( - &owner_config.join_during_genesis, - owner_file, - "join_during_genesis", - bool::from_str, - )? - .unwrap_or(true); - - // We don't require the operator file if the validator is not joining during genesis. - if is_mainnet && !join_during_genesis { - return Ok(ValidatorConfiguration { - owner_account_address: owner_account_address.into(), - owner_account_public_key, - operator_account_address: operator_account_address.into(), - operator_account_public_key, - voter_account_address: voter_account_address.into(), - voter_account_public_key, - consensus_public_key: None, - proof_of_possession: None, - validator_network_public_key: None, - validator_host: None, - full_node_network_public_key: None, - full_node_host: None, - stake_amount, - commission_percentage, - join_during_genesis, - }); - }; - - let operator_file = dir.join(OPERATOR_FILE); - let operator_file = operator_file.as_path(); - let operator_config = client.get::(operator_file)?; - - // Check and convert fields in operator file - let operator_account_address_from_file: AccountAddress = parse_required_option( - &operator_config.operator_account_address, - operator_file, - "operator_account_address", - AccountAddressWithChecks::from_str, - )? - .into(); - let operator_account_public_key_from_file = parse_required_option( - &operator_config.operator_account_public_key, - operator_file, - "operator_account_public_key", - |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), - )?; - let consensus_public_key = parse_required_option( - &operator_config.consensus_public_key, - operator_file, - "consensus_public_key", - |str| parse_key(bls12381::PublicKey::LENGTH, str), - )?; - let consensus_proof_of_possession = parse_required_option( - &operator_config.consensus_proof_of_possession, - operator_file, - "consensus_proof_of_possession", - |str| parse_key(bls12381::ProofOfPossession::LENGTH, str), - )?; - let validator_network_public_key = parse_required_option( - &operator_config.validator_network_public_key, - operator_file, - "validator_network_public_key", - |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), - )?; - let full_node_network_public_key = parse_optional_option( - &operator_config.full_node_network_public_key, - operator_file, - "full_node_network_public_key", - |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), - )?; - - // Verify owner & operator agree on operator - if operator_account_address != operator_account_address_from_file { - return Err( - CliError::CommandArgumentError( - format!("Operator account {} in owner file {} does not match operator account {} in operator file {}", - operator_account_address, - owner_file.display(), - operator_account_address_from_file, - operator_file.display() - ))); - } - if operator_account_public_key != operator_account_public_key_from_file { - return Err( - CliError::CommandArgumentError( - format!("Operator public key {} in owner file {} does not match operator public key {} in operator file {}", - operator_account_public_key, - owner_file.display(), - operator_account_public_key_from_file, - operator_file.display() - ))); - } - - // Build Validator configuration - Ok(ValidatorConfiguration { - owner_account_address: owner_account_address.into(), - owner_account_public_key, - operator_account_address: operator_account_address.into(), - operator_account_public_key, - voter_account_address: voter_account_address.into(), - voter_account_public_key, - consensus_public_key: Some(consensus_public_key), - proof_of_possession: Some(consensus_proof_of_possession), - validator_network_public_key: Some(validator_network_public_key), - validator_host: Some(operator_config.validator_host), - full_node_network_public_key, - full_node_host: operator_config.full_node_host, - stake_amount, - commission_percentage, - join_during_genesis, - }) -} - -// TODO: Move into the Crypto libraries -fn parse_key(num_bytes: usize, str: &str) -> anyhow::Result { - let num_chars: usize = num_bytes * 2; - let mut working = str.trim(); - - // Checks if it has a 0x at the beginning, which is okay - if working.starts_with("0x") { - working = &working[2..]; - } - - match working.len().cmp(&num_chars) { - Ordering::Less => { - anyhow::bail!( - "Key {} is too short {} must be {} hex characters", - str, - working.len(), - num_chars - ) - }, - Ordering::Greater => { - anyhow::bail!( - "Key {} is too long {} must be {} hex characters with or without a 0x in front", - str, - working.len(), - num_chars - ) - }, - Ordering::Equal => {}, - } - - if !working.chars().all(|c| char::is_ascii_hexdigit(&c)) { - anyhow::bail!("Key {} contains a non-hex character", str) - } - - Ok(T::from_encoded_string(str.trim())?) -} - -fn parse_required_option Result, T, E: std::fmt::Display>( - option: &Option, - file: &Path, - field_name: &'static str, - parse: F, -) -> Result { - if let Some(ref field) = option { - parse(field).map_err(|err| { - CliError::CommandArgumentError(format!( - "Field {} is invalid in file {}. Err: {}", - field_name, - file.display(), - err - )) - }) - } else { - Err(CliError::CommandArgumentError(format!( - "File {} is missing {}", - file.display(), - field_name - ))) - } -} - -fn parse_optional_option Result, T, E: std::fmt::Display>( - option: &Option, - file: &Path, - field_name: &'static str, - parse: F, -) -> Result, CliError> { - if let Some(ref field) = option { - parse(field) - .map_err(|err| { - CliError::CommandArgumentError(format!( - "Field {} is invalid in file {}. Err: {}", - field_name, - file.display(), - err - )) - }) - .map(Some) - } else { - Ok(None) - } -} - -fn validate_validators( - layout: &Layout, - validators: &[ValidatorConfiguration], - initialized_accounts: &BTreeMap, - unique_accounts: &mut BTreeSet, - unique_network_keys: &mut HashSet, - unique_consensus_keys: &mut HashSet, - unique_consensus_pops: &mut HashSet, - unique_hosts: &mut HashSet, - seen_owners: &mut BTreeMap, - is_pooled_validator: bool, -) -> CliTypedResult<()> { - // check accounts for validators - let mut errors = vec![]; - - for (i, validator) in validators.iter().enumerate() { - let name = if is_pooled_validator { - format!("Employee Pool #{}", i) - } else { - layout.users.get(i).unwrap().to_string() - }; - - if !initialized_accounts.contains_key(&validator.owner_account_address.into()) { - errors.push(CliError::UnexpectedError(format!( - "Owner {} in validator {} is is not in the balances.yaml file", - validator.owner_account_address, name - ))); - } - if !initialized_accounts.contains_key(&validator.operator_account_address.into()) { - errors.push(CliError::UnexpectedError(format!( - "Operator {} in validator {} is is not in the balances.yaml file", - validator.operator_account_address, name - ))); - } - if !initialized_accounts.contains_key(&validator.voter_account_address.into()) { - errors.push(CliError::UnexpectedError(format!( - "Voter {} in validator {} is is not in the balances.yaml file", - validator.voter_account_address, name - ))); - } - - let owner_balance = initialized_accounts - .get(&validator.owner_account_address.into()) - .unwrap(); - - if seen_owners.contains_key(&validator.owner_account_address.into()) { - errors.push(CliError::UnexpectedError(format!( - "Owner {} in validator {} has been seen before as an owner of validator {}", - validator.owner_account_address, - name, - seen_owners - .get(&validator.owner_account_address.into()) - .unwrap() - ))); - } - seen_owners.insert(validator.owner_account_address.into(), i); - - if unique_accounts.contains(&validator.owner_account_address.into()) { - errors.push(CliError::UnexpectedError(format!( - "Owner '{}' in validator {} has already been seen elsewhere", - validator.owner_account_address, name - ))); - } - unique_accounts.insert(validator.owner_account_address.into()); - - if unique_accounts.contains(&validator.operator_account_address.into()) { - errors.push(CliError::UnexpectedError(format!( - "Operator '{}' in validator {} has already been seen elsewhere", - validator.operator_account_address, name - ))); - } - unique_accounts.insert(validator.operator_account_address.into()); - - // Pooled validators have a combined balance - // TODO: Make this field optional but checked - if !is_pooled_validator && *owner_balance < validator.stake_amount { - errors.push(CliError::UnexpectedError(format!( - "Owner {} in validator {} has less in it's balance {} than the stake amount for the validator {}", - validator.owner_account_address, name, owner_balance, validator.stake_amount - ))); - } - if validator.stake_amount < layout.min_stake { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has stake {} under the min stake {}", - name, validator.stake_amount, layout.min_stake - ))); - } - if validator.stake_amount > layout.max_stake { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has stake {} over the max stake {}", - name, validator.stake_amount, layout.max_stake - ))); - } - - // Ensure that the validator is setup correctly if it's joining in genesis - if validator.join_during_genesis { - if validator.validator_network_public_key.is_none() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} does not have a validator network public key, though it's joining during genesis", - name - ))); - } - if !unique_network_keys.insert(validator.validator_network_public_key.unwrap()) { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a repeated validator network key{}", - name, - validator.validator_network_public_key.unwrap() - ))); - } - - if validator.validator_host.is_none() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} does not have a validator host, though it's joining during genesis", - name - ))); - } - if !unique_hosts.insert(validator.validator_host.as_ref().unwrap().clone()) { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a repeated validator host {:?}", - name, - validator.validator_host.as_ref().unwrap() - ))); - } - - if validator.consensus_public_key.is_none() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} does not have a consensus public key, though it's joining during genesis", - name - ))); - } - if !unique_consensus_keys - .insert(validator.consensus_public_key.as_ref().unwrap().clone()) - { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a repeated a consensus public key {}", - name, - validator.consensus_public_key.as_ref().unwrap() - ))); - } - - if validator.proof_of_possession.is_none() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} does not have a consensus proof of possession, though it's joining during genesis", - name - ))); - } - if !unique_consensus_pops - .insert(validator.proof_of_possession.as_ref().unwrap().clone()) - { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a repeated a consensus proof of possessions {}", - name, - validator.proof_of_possession.as_ref().unwrap() - ))); - } - - match ( - validator.full_node_host.as_ref(), - validator.full_node_network_public_key.as_ref(), - ) { - (None, None) => { - info!("Validator {} does not have a full node setup", name); - }, - (Some(_), None) | (None, Some(_)) => { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a full node host or public key but not both", - name - ))); - }, - (Some(full_node_host), Some(full_node_network_public_key)) => { - // Ensure that the validator and the full node aren't the same - let validator_host = validator.validator_host.as_ref().unwrap(); - let validator_network_public_key = - validator.validator_network_public_key.as_ref().unwrap(); - if validator_host == full_node_host { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a validator and a full node host that are the same {:?}", - name, - validator_host - ))); - } - if !unique_hosts.insert(validator.full_node_host.as_ref().unwrap().clone()) { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a repeated full node host {:?}", - name, - validator.full_node_host.as_ref().unwrap() - ))); - } - - if validator_network_public_key == full_node_network_public_key { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a validator and a full node network public key that are the same {}", - name, - validator_network_public_key - ))); - } - if !unique_network_keys.insert(validator.full_node_network_public_key.unwrap()) - { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a repeated full node network key {}", - name, - validator.full_node_network_public_key.unwrap() - ))); - } - }, - } - } else { - if validator.validator_network_public_key.is_some() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a validator network public key, but it is *NOT* joining during genesis", - name - ))); - } - if validator.validator_host.is_some() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a validator host, but it is *NOT* joining during genesis", - name - ))); - } - if validator.consensus_public_key.is_some() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a consensus public key, but it is *NOT* joining during genesis", - name - ))); - } - if validator.proof_of_possession.is_some() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a consensus proof of possession, but it is *NOT* joining during genesis", - name - ))); - } - if validator.full_node_network_public_key.is_some() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a full node public key, but it is *NOT* joining during genesis", - name - ))); - } - if validator.full_node_host.is_some() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a full node host, but it is *NOT* joining during genesis", - name - ))); - } - } - } - - if errors.is_empty() { - Ok(()) - } else { - eprintln!("{:#?}", errors); - - Err(CliError::UnexpectedError( - "Failed to validate validators".to_string(), - )) - } -} - -fn validate_employee_accounts( - employee_vesting_accounts: &[EmployeePool], - initialized_accounts: &BTreeMap, - unique_accounts: &mut BTreeSet, -) -> CliTypedResult<()> { - // Check accounts for employee accounts - for (i, pool) in employee_vesting_accounts.iter().enumerate() { - let mut total_stake_pool_amount = 0; - for (j, account) in pool.accounts.iter().enumerate() { - if !initialized_accounts.contains_key(account) { - return Err(CliError::UnexpectedError(format!( - "Account #{} '{}' in employee pool #{} is not in the balances.yaml file", - j, account, i - ))); - } - if unique_accounts.contains(account) { - return Err(CliError::UnexpectedError(format!( - "Account #{} '{}' in employee pool #{} has already been seen elsewhere", - j, account, i - ))); - } - unique_accounts.insert(*account); - - total_stake_pool_amount += initialized_accounts.get(account).unwrap(); - } - - if total_stake_pool_amount != pool.validator.validator.stake_amount { - return Err(CliError::UnexpectedError(format!( - "Stake amount {} in employee pool #{} does not match combined of accounts {}", - pool.validator.validator.stake_amount, i, total_stake_pool_amount - ))); - } - - if !initialized_accounts.contains_key(&pool.validator.validator.owner_address) { - return Err(CliError::UnexpectedError(format!( - "Owner address {} in employee pool #{} is is not in the balances.yaml file", - pool.validator.validator.owner_address, i - ))); - } - if !initialized_accounts.contains_key(&pool.validator.validator.operator_address) { - return Err(CliError::UnexpectedError(format!( - "Operator address {} in employee pool #{} is is not in the balances.yaml file", - pool.validator.validator.operator_address, i - ))); - } - if !initialized_accounts.contains_key(&pool.validator.validator.voter_address) { - return Err(CliError::UnexpectedError(format!( - "Voter address {} in employee pool #{} is is not in the balances.yaml file", - pool.validator.validator.voter_address, i - ))); - } - if !initialized_accounts.contains_key(&pool.beneficiary_resetter) { - return Err(CliError::UnexpectedError(format!( - "Beneficiary resetter {} in employee pool #{} is is not in the balances.yaml file", - pool.beneficiary_resetter, i - ))); - } - } - Ok(()) -} diff --git a/m1/m1-cli/src/genesis/tests.rs b/m1/m1-cli/src/genesis/tests.rs deleted file mode 100644 index bb270e6ef..000000000 --- a/m1/m1-cli/src/genesis/tests.rs +++ /dev/null @@ -1,434 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - types::{OptionalPoolAddressArgs, PromptOptions, RngArgs}, - utils::{read_from_file, write_to_file}, - }, - genesis::{ - git::{ - from_yaml, GitOptions, SetupGit, BALANCES_FILE, EMPLOYEE_VESTING_ACCOUNTS_FILE, - FRAMEWORK_NAME, - }, - keys::{GenerateKeys, GenerateLayoutTemplate, SetValidatorConfiguration, PUBLIC_KEYS_FILE}, - GenerateGenesis, - }, - CliCommand, -}; -use aptos_crypto::{ - ed25519::{Ed25519PrivateKey, Ed25519PublicKey}, - PrivateKey, -}; -use aptos_genesis::{ - config::{ - AccountBalanceMap, EmployeePoolConfig, EmployeePoolMap, HostAndPort, Layout, - ValidatorConfiguration, - }, - keys::PublicIdentity, -}; -use aptos_keygen::KeyGen; -use aptos_temppath::TempPath; -use aptos_types::{account_address::AccountAddress, chain_id::ChainId}; -use aptos_vm_genesis::{AccountBalance, TestValidator}; -use std::{ - collections::HashMap, - path::{Path, PathBuf}, - str::FromStr, -}; - -const INITIAL_BALANCE: u64 = 100_000_000_000_000; - -/// Test the E2E genesis flow since it doesn't require a node to run -#[tokio::test] -async fn test_genesis_e2e_flow() { - let is_mainnet = false; - let dir = TempPath::new(); - dir.create_as_dir().unwrap(); - let git_options = create_users(2, 0, &dir, &mut vec![], is_mainnet).await; - - // Now generate genesis - let output_dir = TempPath::new(); - output_dir.create_as_dir().unwrap(); - let output_dir = PathBuf::from(output_dir.path()); - generate_genesis(git_options, output_dir.clone(), is_mainnet).await; - - // TODO: Verify that these are good - let waypoint_file = output_dir.join("waypoint.txt"); - assert!(waypoint_file.exists()); - let genesis_file = output_dir.join("genesis.blob"); - assert!(genesis_file.exists()); -} - -#[tokio::test] -async fn test_mainnet_genesis_e2e_flow() { - let is_mainnet = true; - let dir = TempPath::new(); - dir.create_as_dir().unwrap(); - let git_options = create_users(2, 4, &dir, &mut vec![10, 1], is_mainnet).await; - let account_1 = AccountAddress::from_hex_literal("0x101").unwrap(); - let account_2 = AccountAddress::from_hex_literal("0x102").unwrap(); - let employee_1 = AccountAddress::from_hex_literal("0x201").unwrap(); - let employee_2 = AccountAddress::from_hex_literal("0x202").unwrap(); - let employee_3 = AccountAddress::from_hex_literal("0x203").unwrap(); - let employee_4 = AccountAddress::from_hex_literal("0x204").unwrap(); - - let owner_identity1 = load_identity(dir.path(), "owner-0"); - let owner_identity2 = load_identity(dir.path(), "owner-1"); - let operator_identity1 = load_identity(dir.path(), "operator-0"); - let operator_identity2 = load_identity(dir.path(), "operator-1"); - let voter_identity1 = load_identity(dir.path(), "voter-0"); - let voter_identity2 = load_identity(dir.path(), "voter-1"); - let admin_identity1 = load_identity(dir.path(), "other-0"); - let admin_identity2 = load_identity(dir.path(), "other-1"); - let employee_operator_identity1 = load_identity(dir.path(), "other-2"); - let employee_operator_identity2 = load_identity(dir.path(), "other-3"); - - // Create initial balances and employee vesting account files. - let git_dir = git_options.local_repository_dir.as_ref().unwrap().as_path(); - - create_account_balances_file(PathBuf::from(git_dir), vec![ - owner_identity1.account_address, - owner_identity2.account_address, - operator_identity1.account_address, - operator_identity2.account_address, - voter_identity1.account_address, - voter_identity2.account_address, - account_1, - account_2, - employee_1, - employee_2, - employee_3, - employee_4, - admin_identity1.account_address, - admin_identity2.account_address, - employee_operator_identity1.account_address, - employee_operator_identity2.account_address, - ]) - .await; - create_employee_vesting_accounts_file( - PathBuf::from(git_dir), - &[admin_identity1, admin_identity2], - &[employee_operator_identity1, employee_operator_identity2], - &[vec![employee_1, employee_2], vec![employee_3, employee_4]], - &[true, false], - ) - .await; - - // Now generate genesis - let output_dir = TempPath::new(); - output_dir.create_as_dir().unwrap(); - let output_dir = PathBuf::from(output_dir.path()); - generate_genesis(git_options, output_dir.clone(), is_mainnet).await; - - // TODO: Verify that these are good - let waypoint_file = output_dir.join("waypoint.txt"); - assert!(waypoint_file.exists()); - let genesis_file = output_dir.join("genesis.blob"); - assert!(genesis_file.exists()); -} - -pub fn load_identity(base_dir: &Path, name: &str) -> PublicIdentity { - let path = base_dir.join(name).join(PUBLIC_KEYS_FILE); - from_yaml(&String::from_utf8(read_from_file(path.as_path()).unwrap()).unwrap()).unwrap() -} - -async fn create_users( - num_validators: u8, - num_other_users: u8, - dir: &TempPath, - commission_rates: &mut Vec, - is_mainnet: bool, -) -> GitOptions { - let mut users: HashMap = HashMap::new(); - for i in 0..num_validators { - let name = format!("owner-{}", i); - let output_dir = generate_keys(dir.path(), &name).await; - users.insert(name, output_dir); - - let name = format!("operator-{}", i); - let output_dir = generate_keys(dir.path(), &name).await; - users.insert(name, output_dir); - - let name = format!("voter-{}", i); - let output_dir = generate_keys(dir.path(), &name).await; - users.insert(name, output_dir); - } - for i in 0..num_other_users { - let name = format!("other-{}", i); - let output_dir = generate_keys(dir.path(), &name).await; - users.insert(name, output_dir); - } - - // Get the validator's names - let validator_names = users - .keys() - .map(|key| key.to_string()) - .filter(|name| name.starts_with("owner")) - .collect(); - let mut key_gen = KeyGen::from_seed([num_validators.saturating_add(1); 32]); - - // First step is setup the local git repo - let root_private_key = if !is_mainnet { - Some(key_gen.generate_ed25519_private_key()) - } else { - None - }; - let git_options = - setup_git_dir(root_private_key.as_ref(), validator_names, ChainId::test()).await; - - // Only write validators to folders - for i in 0..num_validators { - let owner_name = format!("owner-{}", i); - let owner_identity = users.get(&owner_name).unwrap().join(PUBLIC_KEYS_FILE); - let operator_identity = users - .get(&format!("operator-{}", i)) - .unwrap() - .join(PUBLIC_KEYS_FILE); - let voter_identity = users - .get(&format!("voter-{}", i)) - .unwrap() - .join(PUBLIC_KEYS_FILE); - let commission_rate = if commission_rates.is_empty() { - 0 - } else { - commission_rates.remove(0) - }; - set_validator_config( - owner_name, - git_options.clone(), - owner_identity.as_path(), - operator_identity.as_path(), - voter_identity.as_path(), - commission_rate, - i as u16, - ) - .await; - } - git_options -} - -/// Generate genesis and waypoint -async fn generate_genesis(git_options: GitOptions, output_dir: PathBuf, mainnet: bool) { - let command = GenerateGenesis { - prompt_options: PromptOptions::yes(), - git_options, - output_dir: Some(output_dir), - mainnet, - }; - let _ = command.execute().await.unwrap(); -} - -/// Setup a temporary repo location and add all required pieces -async fn setup_git_dir( - root_private_key: Option<&Ed25519PrivateKey>, - users: Vec, - chain_id: ChainId, -) -> GitOptions { - let git_options = git_options(); - let layout_file = TempPath::new(); - layout_file.create_as_file().unwrap(); - let layout_file = layout_file.path(); - - create_layout_file( - layout_file, - root_private_key.map(|inner| inner.public_key()), - users, - chain_id, - ) - .await; - let setup_command = SetupGit { - git_options: git_options.clone(), - layout_file: PathBuf::from(layout_file), - }; - - setup_command - .execute() - .await - .expect("Should not fail creating repo folder"); - - // Add framework - add_framework_to_dir(git_options.local_repository_dir.as_ref().unwrap().as_path()); - git_options -} - -/// Add framework to git directory -fn add_framework_to_dir(git_dir: &Path) { - aptos_cached_packages::head_release_bundle() - .write(git_dir.join(FRAMEWORK_NAME)) - .unwrap() -} - -/// Local git options for testing -fn git_options() -> GitOptions { - let temp_path = TempPath::new(); - let path = PathBuf::from(temp_path.path()); - GitOptions { - local_repository_dir: Some(path), - ..Default::default() - } -} - -/// Create a layout file for the repo -async fn create_layout_file( - file: &Path, - root_public_key: Option, - users: Vec, - chain_id: ChainId, -) { - GenerateLayoutTemplate { - output_file: PathBuf::from(file), - prompt_options: PromptOptions::yes(), - } - .execute() - .await - .expect("Expected to create layout template"); - - // Update layout file - let mut layout: Layout = - from_yaml(&String::from_utf8(read_from_file(file).unwrap()).unwrap()).unwrap(); - layout.root_key = root_public_key; - layout.users = users; - layout.chain_id = chain_id; - layout.is_test = true; - layout.total_supply = Some(INITIAL_BALANCE * 16); - - write_to_file( - file, - "Layout file", - serde_yaml::to_string(&layout).unwrap().as_bytes(), - ) - .unwrap(); -} - -/// Generate keys for a "user" -async fn generate_keys(dir: &Path, name: &str) -> PathBuf { - let output_dir = dir.join(name); - let command = GenerateKeys { - pool_address_args: OptionalPoolAddressArgs { pool_address: None }, - rng_args: RngArgs::from_string_seed(name), - prompt_options: PromptOptions::yes(), - output_dir: Some(output_dir.clone()), - }; - let _ = command.execute().await.unwrap(); - output_dir -} - -/// Set validator configuration for a user -async fn set_validator_config( - username: String, - git_options: GitOptions, - owner_identity_file: &Path, - operator_identity_file: &Path, - voter_identity_file: &Path, - commission_percentage: u64, - port: u16, -) { - let command = SetValidatorConfiguration { - username, - git_options, - owner_public_identity_file: Some(owner_identity_file.to_path_buf()), - validator_host: HostAndPort::from_str(&format!("localhost:{}", port)).unwrap(), - stake_amount: 100_000_000_000_000, - full_node_host: None, - operator_public_identity_file: Some(operator_identity_file.to_path_buf()), - voter_public_identity_file: Some(voter_identity_file.to_path_buf()), - commission_percentage, - join_during_genesis: true, - }; - - command.execute().await.unwrap() -} - -async fn create_account_balances_file(path: PathBuf, addresses: Vec) { - let account_balances: Vec = addresses - .iter() - .map(|account_address| AccountBalance { - account_address: *account_address, - balance: INITIAL_BALANCE, - }) - .collect(); - - let balance_map = AccountBalanceMap::try_from(account_balances).unwrap(); - - write_to_file( - &path.join(BALANCES_FILE), - BALANCES_FILE, - serde_yaml::to_string(&balance_map).unwrap().as_bytes(), - ) - .unwrap(); -} - -async fn create_employee_vesting_accounts_file( - path: PathBuf, - admin_identities: &[PublicIdentity], - operator_identities: &[PublicIdentity], - employee_groups: &[Vec], - join_during_genesis: &[bool], -) { - TestValidator::new_test_set(Some(employee_groups.len()), Some(INITIAL_BALANCE)); - let employee_vesting_accounts: Vec<_> = employee_groups - .iter() - .enumerate() - .map(|(index, accounts)| { - let admin_identity = admin_identities[index].clone(); - let operator_identity = operator_identities[index].clone(); - let validator_config = if *join_during_genesis.get(index).unwrap() { - ValidatorConfiguration { - owner_account_address: admin_identity.account_address.into(), - owner_account_public_key: admin_identity.account_public_key.clone(), - operator_account_address: operator_identity.account_address.into(), - operator_account_public_key: operator_identity.account_public_key.clone(), - voter_account_address: admin_identity.account_address.into(), - voter_account_public_key: admin_identity.account_public_key, - consensus_public_key: operator_identity.consensus_public_key, - proof_of_possession: operator_identity.consensus_proof_of_possession, - validator_network_public_key: operator_identity.validator_network_public_key, - validator_host: Some(HostAndPort::from_str("localhost:8080").unwrap()), - full_node_network_public_key: operator_identity.full_node_network_public_key, - full_node_host: Some(HostAndPort::from_str("localhost:8081").unwrap()), - stake_amount: 2 * INITIAL_BALANCE, - commission_percentage: 0, - join_during_genesis: true, - } - } else { - ValidatorConfiguration { - owner_account_address: admin_identity.account_address.into(), - owner_account_public_key: admin_identity.account_public_key.clone(), - operator_account_address: operator_identity.account_address.into(), - operator_account_public_key: operator_identity.account_public_key, - voter_account_address: admin_identity.account_address.into(), - voter_account_public_key: admin_identity.account_public_key, - consensus_public_key: None, - proof_of_possession: None, - validator_network_public_key: None, - validator_host: None, - full_node_network_public_key: None, - full_node_host: None, - stake_amount: 2 * INITIAL_BALANCE, - commission_percentage: 0, - join_during_genesis: false, - } - }; - - EmployeePoolConfig { - accounts: accounts.iter().map(|addr| addr.into()).collect(), - validator: validator_config, - vesting_schedule_numerators: vec![3, 3, 3, 3, 1], - vesting_schedule_denominator: 48, - beneficiary_resetter: AccountAddress::from_hex_literal("0x101").unwrap().into(), - } - }) - .collect(); - let employee_vesting_map = EmployeePoolMap { - inner: employee_vesting_accounts, - }; - write_to_file( - &path.join(EMPLOYEE_VESTING_ACCOUNTS_FILE), - EMPLOYEE_VESTING_ACCOUNTS_FILE, - serde_yaml::to_string(&employee_vesting_map) - .unwrap() - .as_bytes(), - ) - .unwrap(); -} diff --git a/m1/m1-cli/src/genesis/tools.rs b/m1/m1-cli/src/genesis/tools.rs deleted file mode 100644 index 5f3f8ed5f..000000000 --- a/m1/m1-cli/src/genesis/tools.rs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - types::PromptOptions, - utils::{dir_default_to_current, write_to_file}, - }, - genesis::{ - get_validator_configs, - git::{GitOptions, EMPLOYEE_VESTING_ACCOUNTS_FILE, LAYOUT_FILE}, - parse_error, - }, - CliCommand, CliTypedResult, -}; -use aptos_genesis::config::{EmployeePoolMap, Layout}; -use aptos_sdk::move_types::account_address::AccountAddress; -use aptos_types::account_address::{create_vesting_pool_address, default_stake_pool_address}; -use async_trait::async_trait; -use clap::Parser; -use std::{ - collections::BTreeMap, - path::{Path, PathBuf}, -}; - -const POOL_ADDRESSES: &str = "pool-addresses.yaml"; -const EMPLOYEE_POOL_ADDRESSES: &str = "employee-pool-addresses.yaml"; - -/// Get pool addresses from a mainnet genesis setup -/// -/// Outputs all pool addresses to a file from the genesis files -#[derive(Parser)] -pub struct PoolAddresses { - /// Output directory for pool addresses - #[clap(long, parse(from_os_str))] - output_dir: Option, - - #[clap(flatten)] - prompt_options: PromptOptions, - #[clap(flatten)] - git_options: GitOptions, -} - -#[async_trait] -impl CliCommand> for PoolAddresses { - fn command_name(&self) -> &'static str { - "GetPoolAddresses" - } - - async fn execute(self) -> CliTypedResult> { - let output_dir = dir_default_to_current(self.output_dir.clone())?; - let client = self.git_options.get_client()?; - let layout: Layout = client.get(Path::new(LAYOUT_FILE))?; - let employee_vesting_accounts: EmployeePoolMap = - client.get(Path::new(EMPLOYEE_VESTING_ACCOUNTS_FILE))?; - let validators = get_validator_configs(&client, &layout, true).map_err(parse_error)?; - - let mut address_to_pool = BTreeMap::::new(); - - for validator in validators { - let stake_pool_address = default_stake_pool_address( - validator.owner_account_address.into(), - validator.operator_account_address.into(), - ); - address_to_pool.insert(validator.owner_account_address.into(), stake_pool_address); - } - - let mut employee_address_to_pool = BTreeMap::::new(); - - for employee_pool in employee_vesting_accounts.inner.iter() { - let stake_pool_address = create_vesting_pool_address( - employee_pool.validator.owner_account_address.into(), - employee_pool.validator.operator_account_address.into(), - 0, - &[], - ); - - employee_address_to_pool.insert( - employee_pool.validator.owner_account_address.into(), - stake_pool_address, - ); - } - - let pool_addresses_file = output_dir.join(POOL_ADDRESSES); - let employee_pool_addresses_file = output_dir.join(EMPLOYEE_POOL_ADDRESSES); - - write_to_file( - pool_addresses_file.as_path(), - POOL_ADDRESSES, - serde_yaml::to_string(&address_to_pool)?.as_bytes(), - )?; - write_to_file( - employee_pool_addresses_file.as_path(), - EMPLOYEE_POOL_ADDRESSES, - serde_yaml::to_string(&employee_address_to_pool)?.as_bytes(), - )?; - - Ok(vec![pool_addresses_file, employee_pool_addresses_file]) - } -} diff --git a/m1/m1-cli/src/governance/mod.rs b/m1/m1-cli/src/governance/mod.rs deleted file mode 100644 index b399696a6..000000000 --- a/m1/m1-cli/src/governance/mod.rs +++ /dev/null @@ -1,1061 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -#[cfg(feature = "no-upload-proposal")] -use crate::common::utils::read_from_file; -use crate::{ - common::{ - types::{ - CliError, CliTypedResult, MovePackageDir, PoolAddressArgs, ProfileOptions, - PromptOptions, RestOptions, TransactionOptions, TransactionSummary, - }, - utils::prompt_yes_with_override, - }, - move_tool::{FrameworkPackageArgs, IncludedArtifacts}, - CliCommand, CliResult, -}; -use aptos_cached_packages::aptos_stdlib; -use aptos_crypto::HashValue; -use aptos_framework::{BuildOptions, BuiltPackage, ReleasePackage}; -use aptos_logger::warn; -use aptos_rest_client::{ - aptos_api_types::{Address, HexEncodedBytes, U128, U64}, - Client, Transaction, -}; -use aptos_sdk::move_types::language_storage::CORE_CODE_ADDRESS; -use aptos_types::{ - account_address::AccountAddress, - event::EventHandle, - governance::VotingRecords, - stake_pool::StakePool, - state_store::table::TableHandle, - transaction::{Script, TransactionPayload}, -}; -use async_trait::async_trait; -use clap::Parser; -use move_core_types::transaction_argument::TransactionArgument; -use reqwest::Url; -use serde::{Deserialize, Serialize}; -use std::{ - collections::BTreeMap, - fmt::Formatter, - fs, - path::{Path, PathBuf}, -}; -use tempfile::TempDir; - -/// Tool for on-chain governance -/// -/// This tool allows voters that have stake to vote the ability to -/// propose changes to the chain, as well as vote and execute these -/// proposals. -#[derive(Parser)] -pub enum GovernanceTool { - Propose(SubmitProposal), - Vote(SubmitVote), - ShowProposal(ViewProposal), - ListProposals(ListProposals), - VerifyProposal(VerifyProposal), - ExecuteProposal(ExecuteProposal), - GenerateUpgradeProposal(GenerateUpgradeProposal), - ApproveExecutionHash(ApproveExecutionHash), -} - -impl GovernanceTool { - pub async fn execute(self) -> CliResult { - use GovernanceTool::*; - match self { - Propose(tool) => tool.execute_serialized().await, - Vote(tool) => tool.execute_serialized().await, - ExecuteProposal(tool) => tool.execute_serialized().await, - GenerateUpgradeProposal(tool) => tool.execute_serialized_success().await, - ShowProposal(tool) => tool.execute_serialized().await, - ListProposals(tool) => tool.execute_serialized().await, - VerifyProposal(tool) => tool.execute_serialized().await, - ApproveExecutionHash(tool) => tool.execute_serialized().await, - } - } -} - -/// View a known on-chain governance proposal -/// -/// This command will return the proposal requested as well as compute -/// the hash of the metadata to determine whether it was verified or not. -#[derive(Parser)] -pub struct ViewProposal { - /// The identifier of the onchain governance proposal - #[clap(long)] - proposal_id: u64, - - #[clap(flatten)] - rest_options: RestOptions, - #[clap(flatten)] - profile: ProfileOptions, -} - -#[async_trait] -impl CliCommand for ViewProposal { - fn command_name(&self) -> &'static str { - "ViewProposal" - } - - async fn execute(mut self) -> CliTypedResult { - // Get proposal - let client = self.rest_options.client(&self.profile)?; - let forum = client - .get_account_resource_bcs::( - AccountAddress::ONE, - "0x1::voting::VotingForum<0x1::governance_proposal::GovernanceProposal>", - ) - .await? - .into_inner(); - let voting_table = forum.table_handle.0; - - let proposal: Proposal = get_proposal(&client, voting_table, self.proposal_id) - .await? - .into(); - - let metadata_hash = proposal.metadata.get("metadata_hash").unwrap(); - let metadata_url = proposal.metadata.get("metadata_location").unwrap(); - - // Compute the hash and verify accordingly - let mut metadata_verified = false; - let mut actual_metadata_hash = "Unable to fetch metadata url".to_string(); - let mut actual_metadata = None; - if let Ok(url) = Url::parse(metadata_url) { - if let Ok(bytes) = get_metadata_from_url(&url).await { - let hash = HashValue::sha3_256_of(&bytes); - metadata_verified = metadata_hash == &hash.to_hex(); - actual_metadata_hash = hash.to_hex(); - if let Ok(metadata) = String::from_utf8(bytes) { - actual_metadata = Some(metadata); - } - } - } - - Ok(VerifiedProposal { - metadata_verified, - actual_metadata_hash, - actual_metadata, - proposal, - }) - } -} - -/// List the last 100 visible onchain proposals -/// -/// Note, if the full node you are talking to is pruning data, it may not have some of the -/// proposals show here -#[derive(Parser)] -pub struct ListProposals { - #[clap(flatten)] - rest_options: RestOptions, - #[clap(flatten)] - profile: ProfileOptions, -} - -#[async_trait] -impl CliCommand> for ListProposals { - fn command_name(&self) -> &'static str { - "ListProposals" - } - - async fn execute(mut self) -> CliTypedResult> { - // List out known proposals based on events - let client = self.rest_options.client(&self.profile)?; - - let events = client - .get_account_events_bcs( - AccountAddress::ONE, - "0x1::aptos_governance::GovernanceEvents", - "create_proposal_events", - None, - Some(100), - ) - .await? - .into_inner(); - let mut proposals = vec![]; - - for event in &events { - match bcs::from_bytes::(event.event.event_data()) { - Ok(valid_event) => proposals.push(valid_event.into()), - Err(err) => { - eprintln!( - "Event: {:?} cannot be parsed as a proposal: {:?}", - event, err - ) - }, - } - } - - // TODO: Show more information about proposal? - Ok(proposals) - } -} - -/// Verify a proposal given the source code of the script -/// -/// The script's bytecode or source can be provided and it will -/// verify whether the hash matches the onchain hash -#[derive(Parser)] -pub struct VerifyProposal { - /// The id of the onchain proposal - #[clap(long)] - pub(crate) proposal_id: u64, - - #[clap(flatten)] - pub(crate) compile_proposal_args: CompileScriptFunction, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile: ProfileOptions, - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, -} - -#[async_trait] -impl CliCommand for VerifyProposal { - fn command_name(&self) -> &'static str { - "VerifyProposal" - } - - async fn execute(mut self) -> CliTypedResult { - // Compile local first to get the hash - let (_, hash) = self - .compile_proposal_args - .compile("SubmitProposal", self.prompt_options)?; - - // Retrieve the onchain proposal - let client = self.rest_options.client(&self.profile)?; - let forum = client - .get_account_resource_bcs::( - AccountAddress::ONE, - "0x1::voting::VotingForum<0x1::governance_proposal::GovernanceProposal>", - ) - .await? - .into_inner(); - let voting_table = forum.table_handle.0; - - let proposal: Proposal = get_proposal(&client, voting_table, self.proposal_id) - .await? - .into(); - - // Compare the hashes - let computed_hash = hash.to_hex(); - let onchain_hash = proposal.execution_hash; - - Ok(VerifyProposalResponse { - verified: computed_hash == onchain_hash, - computed_hash, - onchain_hash, - }) - } -} - -async fn get_proposal( - client: &aptos_rest_client::Client, - voting_table: AccountAddress, - proposal_id: u64, -) -> CliTypedResult { - let json = client - .get_table_item( - voting_table, - "u64", - "0x1::voting::Proposal<0x1::governance_proposal::GovernanceProposal>", - format!("{}", proposal_id), - ) - .await? - .into_inner(); - serde_json::from_value(json) - .map_err(|err| CliError::CommandArgumentError(format!("Failed to parse proposal {}", err))) -} - -/// Submit a governance proposal -#[derive(Parser)] -pub struct SubmitProposal { - /// Location of the JSON metadata of the proposal - /// - /// If this location does not keep the metadata in the exact format, it will be less likely - /// that voters will approve this proposal, as they won't be able to verify it. - #[clap(long)] - pub(crate) metadata_url: Url, - - #[cfg(feature = "no-upload-proposal")] - /// A JSON file to be uploaded later at the metadata URL - /// - /// If this does not match properly, voters may choose to vote no. For real proposals, - /// it is better to already have it uploaded at the URL. - #[clap(long)] - pub(crate) metadata_path: Option, - - #[clap(long)] - pub(crate) is_multi_step: bool, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) pool_address_args: PoolAddressArgs, - #[clap(flatten)] - pub(crate) compile_proposal_args: CompileScriptFunction, -} - -#[async_trait] -impl CliCommand for SubmitProposal { - fn command_name(&self) -> &'static str { - "SubmitProposal" - } - - async fn execute(mut self) -> CliTypedResult { - let (_bytecode, script_hash) = self - .compile_proposal_args - .compile("SubmitProposal", self.txn_options.prompt_options)?; - - // Validate the proposal metadata - let (metadata, metadata_hash) = self.get_metadata().await?; - - println!( - "{}\n\tMetadata Hash: {}\n\tScript Hash: {}", - metadata, metadata_hash, script_hash - ); - prompt_yes_with_override( - "Do you want to submit this proposal?", - self.txn_options.prompt_options, - )?; - - let txn: Transaction = if self.is_multi_step { - self.txn_options - .submit_transaction(aptos_stdlib::aptos_governance_create_proposal_v2( - self.pool_address_args.pool_address, - script_hash.to_vec(), - self.metadata_url.to_string().as_bytes().to_vec(), - metadata_hash.to_hex().as_bytes().to_vec(), - true, - )) - .await? - } else { - self.txn_options - .submit_transaction(aptos_stdlib::aptos_governance_create_proposal( - self.pool_address_args.pool_address, - script_hash.to_vec(), - self.metadata_url.to_string().as_bytes().to_vec(), - metadata_hash.to_hex().as_bytes().to_vec(), - )) - .await? - }; - let txn_summary = TransactionSummary::from(&txn); - if let Transaction::UserTransaction(inner) = txn { - // Find event with proposal id - let proposal_id = if let Some(event) = inner.events.into_iter().find(|event| { - event.typ.to_string().as_str() == "0x1::aptos_governance::CreateProposalEvent" - }) { - let data: CreateProposalEvent = - serde_json::from_value(event.data).map_err(|_| { - CliError::UnexpectedError( - "Failed to parse Proposal event to get ProposalId".to_string(), - ) - })?; - Some(data.proposal_id.0) - } else { - warn!("No proposal event found to find proposal id"); - None - }; - - return Ok(ProposalSubmissionSummary { - proposal_id, - transaction: txn_summary, - }); - } - Err(CliError::UnexpectedError( - "Unable to find parse proposal transaction output".to_string(), - )) - } -} - -impl SubmitProposal { - /// Retrieve metadata and validate it - async fn get_metadata(&self) -> CliTypedResult<(ProposalMetadata, HashValue)> { - #[cfg(feature = "no-upload-proposal")] - let bytes = if let Some(ref path) = self.metadata_path { - read_from_file(path)? - } else { - get_metadata_from_url(&self.metadata_url).await? - }; - #[cfg(not(feature = "no-upload-proposal"))] - let bytes = get_metadata_from_url(&self.metadata_url).await?; - - let metadata: ProposalMetadata = serde_json::from_slice(&bytes).map_err(|err| { - CliError::CommandArgumentError(format!( - "Metadata is not in a proper JSON format: {}", - err - )) - })?; - Url::parse(&metadata.source_code_url).map_err(|err| { - CliError::CommandArgumentError(format!( - "Source code URL {} is invalid {}", - metadata.source_code_url, err - )) - })?; - Url::parse(&metadata.discussion_url).map_err(|err| { - CliError::CommandArgumentError(format!( - "Discussion URL {} is invalid {}", - metadata.discussion_url, err - )) - })?; - let metadata_hash = HashValue::sha3_256_of(&bytes); - Ok((metadata, metadata_hash)) - } -} - -/// Retrieve the Metadata from the given URL -async fn get_metadata_from_url(metadata_url: &Url) -> CliTypedResult> { - let client = reqwest::ClientBuilder::default() - .tls_built_in_root_certs(true) - .build() - .map_err(|err| CliError::UnexpectedError(format!("Failed to build HTTP client {}", err)))?; - client - .get(metadata_url.clone()) - .send() - .await - .map_err(|err| { - CliError::CommandArgumentError(format!( - "Failed to fetch metadata url {}: {}", - metadata_url, err - )) - })? - .bytes() - .await - .map(|b| b.to_vec()) - .map_err(|err| { - CliError::CommandArgumentError(format!( - "Failed to fetch metadata url {}: {}", - metadata_url, err - )) - }) -} - -#[derive(Debug, Deserialize, Serialize)] -struct CreateProposalEvent { - proposal_id: U64, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct ProposalSubmissionSummary { - proposal_id: Option, - #[serde(flatten)] - transaction: TransactionSummary, -} - -/// Submit a vote on a proposal -/// -/// Votes can only be given on proposals that are currently open for voting. You can vote -/// with `--yes` for a yes vote, and `--no` for a no vote. -#[derive(Parser)] -pub struct SubmitVote { - /// Id of the proposal to vote on - #[clap(long)] - pub(crate) proposal_id: u64, - - /// Vote to accept the proposal - #[clap(long, group = "vote")] - pub(crate) yes: bool, - - /// Vote to reject the proposal - #[clap(long, group = "vote")] - pub(crate) no: bool, - - /// Space separated list of pool addresses. - #[clap(long, multiple_values = true, parse(try_from_str=crate::common::types::load_account_arg))] - pub(crate) pool_addresses: Vec, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand> for SubmitVote { - fn command_name(&self) -> &'static str { - "SubmitVote" - } - - async fn execute(mut self) -> CliTypedResult> { - let (vote_str, vote) = match (self.yes, self.no) { - (true, false) => ("Yes", true), - (false, true) => ("No", false), - (_, _) => { - return Err(CliError::CommandArgumentError( - "Must choose only either --yes or --no".to_string(), - )); - }, - }; - - let client: &Client = &self - .txn_options - .rest_options - .client(&self.txn_options.profile_options)?; - let proposal_id = self.proposal_id; - let voting_records = client - .get_account_resource_bcs::( - CORE_CODE_ADDRESS, - "0x1::aptos_governance::VotingRecords", - ) - .await - .unwrap() - .into_inner() - .votes; - - let mut summaries: Vec = vec![]; - for pool_address in self.pool_addresses { - let voting_record = client - .get_table_item( - voting_records, - "0x1::aptos_governance::RecordKey", - "bool", - VotingRecord { - proposal_id: proposal_id.to_string(), - stake_pool: pool_address, - }, - ) - .await; - let voted = if let Ok(voting_record) = voting_record { - voting_record.into_inner().as_bool().unwrap() - } else { - false - }; - if voted { - println!("Stake pool {} already voted", pool_address); - continue; - } - - let stake_pool = client - .get_account_resource_bcs::(pool_address, "0x1::stake::StakePool") - .await? - .into_inner(); - let voting_power = stake_pool.get_governance_voting_power(); - - prompt_yes_with_override( - &format!( - "Vote {} with voting power = {} from stake pool {}?", - vote_str, voting_power, pool_address - ), - self.txn_options.prompt_options, - )?; - - summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::aptos_governance_vote( - pool_address, - proposal_id, - vote, - )) - .await - .map(TransactionSummary::from)?, - ); - } - Ok(summaries) - } -} - -/// Submit a transaction to approve a proposal's script hash to bypass the transaction size limit. -/// This is needed for upgrading large packages such as aptos-framework. -#[derive(Parser)] -pub struct ApproveExecutionHash { - /// Id of the proposal to vote on - #[clap(long)] - pub(crate) proposal_id: u64, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for ApproveExecutionHash { - fn command_name(&self) -> &'static str { - "ApproveExecutionHash" - } - - async fn execute(mut self) -> CliTypedResult { - Ok(self - .txn_options - .submit_transaction( - aptos_stdlib::aptos_governance_add_approved_script_hash_script(self.proposal_id), - ) - .await - .map(TransactionSummary::from)?) - } -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct VotingRecord { - proposal_id: String, - stake_pool: AccountAddress, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ProposalMetadata { - title: String, - description: String, - source_code_url: String, - discussion_url: String, -} - -impl std::fmt::Display for ProposalMetadata { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Proposal:\n\tTitle:{}\n\tDescription:{}\n\tSource code URL:{}\n\tDiscussion URL:{}", - self.title, self.description, self.source_code_url, self.discussion_url - ) - } -} - -fn compile_in_temp_dir( - script_name: &str, - script_path: &Path, - framework_package_args: &FrameworkPackageArgs, - prompt_options: PromptOptions, - bytecode_version: Option, -) -> CliTypedResult<(Vec, HashValue)> { - // Make a temporary directory for compilation - let temp_dir = TempDir::new().map_err(|err| { - CliError::UnexpectedError(format!("Failed to create temporary directory {}", err)) - })?; - - // Initialize a move directory - let package_dir = temp_dir.path(); - framework_package_args.init_move_dir( - package_dir, - script_name, - BTreeMap::new(), - prompt_options, - )?; - - // Insert the new script - let sources_dir = package_dir.join("sources"); - let new_script_path = if let Some(file_name) = script_path.file_name() { - sources_dir.join(file_name) - } else { - // If for some reason we can't get the move file - sources_dir.join("script.move") - }; - fs::copy(script_path, new_script_path.as_path()).map_err(|err| { - CliError::IO( - format!( - "Failed to copy {} to {}", - script_path.display(), - new_script_path.display() - ), - err, - ) - })?; - - // Compile the script - compile_script( - framework_package_args.skip_fetch_latest_git_deps, - package_dir, - bytecode_version, - ) -} - -fn compile_script( - skip_fetch_latest_git_deps: bool, - package_dir: &Path, - bytecode_version: Option, -) -> CliTypedResult<(Vec, HashValue)> { - let build_options = BuildOptions { - with_srcs: false, - with_abis: false, - with_source_maps: false, - with_error_map: false, - skip_fetch_latest_git_deps, - bytecode_version, - ..BuildOptions::default() - }; - - let pack = BuiltPackage::build(package_dir.to_path_buf(), build_options) - .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?; - - let scripts_count = pack.script_count(); - - if scripts_count != 1 { - return Err(CliError::UnexpectedError(format!( - "Only one script can be prepared a time. Make sure one and only one script file \ - is included in the Move package. Found {} scripts.", - scripts_count - ))); - } - - let bytes = pack.extract_script_code().pop().unwrap(); - let hash = HashValue::sha3_256_of(bytes.as_slice()); - Ok((bytes, hash)) -} - -/// Execute a proposal that has passed voting requirements -#[derive(Parser)] -pub struct ExecuteProposal { - /// Proposal Id being executed - #[clap(long)] - pub(crate) proposal_id: u64, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) compile_proposal_args: CompileScriptFunction, -} - -#[async_trait] -impl CliCommand for ExecuteProposal { - fn command_name(&self) -> &'static str { - "ExecuteProposal" - } - - async fn execute(mut self) -> CliTypedResult { - let (bytecode, _script_hash) = self - .compile_proposal_args - .compile("ExecuteProposal", self.txn_options.prompt_options)?; - // TODO: Check hash so we don't do a failed roundtrip? - - let args = vec![TransactionArgument::U64(self.proposal_id)]; - let txn = TransactionPayload::Script(Script::new(bytecode, vec![], args)); - - self.txn_options - .submit_transaction(txn) - .await - .map(TransactionSummary::from) - } -} - -/// Compile a specified script. -#[derive(Parser)] -pub struct CompileScriptFunction { - /// Path to the Move script for the proposal - #[clap(long, group = "script", parse(from_os_str))] - pub script_path: Option, - - /// Path to the Move script for the proposal - #[clap(long, group = "script", parse(from_os_str))] - pub compiled_script_path: Option, - - #[clap(flatten)] - pub(crate) framework_package_args: FrameworkPackageArgs, - - #[clap(long)] - pub(crate) bytecode_version: Option, -} - -impl CompileScriptFunction { - pub(crate) fn compile( - &self, - script_name: &str, - prompt_options: PromptOptions, - ) -> CliTypedResult<(Vec, HashValue)> { - if let Some(compiled_script_path) = &self.compiled_script_path { - let bytes = std::fs::read(compiled_script_path).map_err(|e| { - CliError::IO(format!("Unable to read {:?}", self.compiled_script_path), e) - })?; - let hash = HashValue::sha3_256_of(bytes.as_slice()); - return Ok((bytes, hash)); - } - - // Check script file - let script_path = self - .script_path - .as_ref() - .ok_or_else(|| { - CliError::CommandArgumentError( - "Must choose either --compiled-script-path or --script-path".to_string(), - ) - })? - .as_path(); - if !script_path.exists() { - return Err(CliError::CommandArgumentError(format!( - "{} does not exist", - script_path.display() - ))); - } else if script_path.is_dir() { - return Err(CliError::CommandArgumentError(format!( - "{} is a directory", - script_path.display() - ))); - } - - // Compile script - compile_in_temp_dir( - script_name, - script_path, - &self.framework_package_args, - prompt_options, - self.bytecode_version, - ) - } -} - -/// Generates a package upgrade proposal script. -#[derive(Parser)] -pub struct GenerateUpgradeProposal { - /// Address of the account which the proposal addresses. - #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] - pub(crate) account: AccountAddress, - - /// Where to store the generated proposal - #[clap(long, parse(from_os_str), default_value = "proposal.move")] - pub(crate) output: PathBuf, - - /// What artifacts to include in the package. This can be one of `none`, `sparse`, and - /// `all`. `none` is the most compact form and does not allow to reconstruct a source - /// package from chain; `sparse` is the minimal set of artifacts needed to reconstruct - /// a source package; `all` includes all available artifacts. The choice of included - /// artifacts heavily influences the size and therefore gas cost of publishing: `none` - /// is the size of bytecode alone; `sparse` is roughly 2 times as much; and `all` 3-4 - /// as much. - #[clap(long, default_value_t = IncludedArtifacts::Sparse)] - pub(crate) included_artifacts: IncludedArtifacts, - - /// Generate the script for mainnet governance proposal by default or generate the upgrade script for testnet. - #[clap(long)] - pub(crate) testnet: bool, - - #[clap(long, default_value = "")] - pub(crate) next_execution_hash: String, - - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, -} - -#[async_trait] -impl CliCommand<()> for GenerateUpgradeProposal { - fn command_name(&self) -> &'static str { - "GenerateUpgradeProposal" - } - - async fn execute(self) -> CliTypedResult<()> { - let GenerateUpgradeProposal { - move_options, - account, - included_artifacts, - output, - testnet, - next_execution_hash, - } = self; - let package_path = move_options.get_package_path()?; - let options = included_artifacts.build_options( - move_options.skip_fetch_latest_git_deps, - move_options.named_addresses(), - move_options.bytecode_version, - ); - let package = BuiltPackage::build(package_path, options)?; - let release = ReleasePackage::new(package)?; - - // If we're generating a single-step proposal on testnet - if testnet && next_execution_hash.is_empty() { - release.generate_script_proposal_testnet(account, output)?; - // If we're generating a single-step proposal on mainnet - } else if next_execution_hash.is_empty() { - release.generate_script_proposal(account, output)?; - // If we're generating a multi-step proposal - } else { - let next_execution_hash_bytes = hex::decode(next_execution_hash)?; - release.generate_script_proposal_multi_step( - account, - output, - next_execution_hash_bytes, - )?; - }; - Ok(()) - } -} - -/// Generate execution hash for a specified script. -#[derive(Parser)] -pub struct GenerateExecutionHash { - #[clap(long)] - pub script_path: Option, -} - -impl GenerateExecutionHash { - pub fn generate_hash(&self) -> CliTypedResult<(Vec, HashValue)> { - CompileScriptFunction { - script_path: self.script_path.clone(), - compiled_script_path: None, - framework_package_args: FrameworkPackageArgs { - framework_git_rev: None, - framework_local_dir: Option::from({ - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.pop(); - path.pop(); - path.join("aptos-move") - .join("framework") - .join("aptos-framework") - .canonicalize() - .map_err(|err| { - CliError::IO( - format!("Failed to canonicalize aptos framework path: {:?}", path), - err, - ) - })? - }), - skip_fetch_latest_git_deps: false, - }, - bytecode_version: None, - } - .compile("execution_hash", PromptOptions::yes()) - } -} - -/// Response for `verify proposal` -#[derive(Serialize, Deserialize, Debug)] -pub struct VerifyProposalResponse { - pub verified: bool, - pub computed_hash: String, - pub onchain_hash: String, -} - -/// Voting forum onchain type -/// -/// TODO: Move to a shared location -#[derive(Serialize, Deserialize, Debug)] -pub struct VotingForum { - table_handle: TableHandle, - events: VotingEvents, - next_proposal_id: u64, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct VotingEvents { - create_proposal_events: EventHandle, - register_forum_events: EventHandle, - resolve_proposal_events: EventHandle, - vote_events: EventHandle, -} - -/// Summary of proposal from the listing events for `ListProposals` -#[derive(Serialize, Deserialize, Debug)] -struct ProposalSummary { - proposer: AccountAddress, - stake_pool: AccountAddress, - proposal_id: u64, - execution_hash: String, - proposal_metadata: BTreeMap, -} - -impl From for ProposalSummary { - fn from(event: CreateProposalFullEvent) -> Self { - let proposal_metadata = event - .proposal_metadata - .into_iter() - .map(|(key, value)| (key, String::from_utf8(value).unwrap())) - .collect(); - ProposalSummary { - proposer: event.proposer, - stake_pool: event.stake_pool, - proposal_id: event.proposal_id, - execution_hash: hex::encode(event.execution_hash), - proposal_metadata, - } - } -} - -#[derive(Deserialize)] -struct CreateProposalFullEvent { - proposer: AccountAddress, - stake_pool: AccountAddress, - proposal_id: u64, - execution_hash: Vec, - proposal_metadata: Vec<(String, Vec)>, -} - -/// A proposal and the verified information about it -#[derive(Serialize, Deserialize, Debug)] -pub struct VerifiedProposal { - metadata_verified: bool, - actual_metadata_hash: String, - actual_metadata: Option, - proposal: Proposal, -} - -/// A reformatted type that has human readable version of the proposal onchain -#[derive(Serialize, Deserialize, Debug)] -pub struct Proposal { - proposer: AccountAddress, - metadata: BTreeMap, - creation_time_secs: u64, - execution_hash: String, - min_vote_threshold: u128, - expiration_secs: u64, - early_resolution_vote_threshold: Option, - yes_votes: u128, - no_votes: u128, - is_resolved: bool, - resolution_time_secs: u64, -} - -impl From for Proposal { - fn from(proposal: JsonProposal) -> Self { - let metadata = proposal - .metadata - .data - .into_iter() - .map(|pair| { - let value = match pair.key.as_str() { - "metadata_hash" => String::from_utf8(pair.value.0) - .unwrap_or_else(|_| "Failed to parse utf8".to_string()), - "metadata_location" => String::from_utf8(pair.value.0) - .unwrap_or_else(|_| "Failed to parse utf8".to_string()), - "RESOLVABLE_TIME_METADATA_KEY" => bcs::from_bytes::(pair.value.inner()) - .map(|inner| inner.to_string()) - .unwrap_or_else(|_| "Failed to parse u64".to_string()), - _ => pair.value.to_string(), - }; - (pair.key, value) - }) - .collect(); - - Proposal { - proposer: proposal.proposer.into(), - metadata, - creation_time_secs: proposal.creation_time_secs.into(), - execution_hash: format!("{:x}", proposal.execution_hash), - min_vote_threshold: proposal.min_vote_threshold.into(), - expiration_secs: proposal.expiration_secs.into(), - early_resolution_vote_threshold: proposal - .early_resolution_vote_threshold - .vec - .first() - .map(|inner| inner.0), - yes_votes: proposal.yes_votes.into(), - no_votes: proposal.no_votes.into(), - is_resolved: proposal.is_resolved, - resolution_time_secs: proposal.resolution_time_secs.into(), - } - } -} - -/// An ugly JSON parsing version for from the JSON API -#[derive(Serialize, Deserialize, Debug)] -struct JsonProposal { - creation_time_secs: U64, - early_resolution_vote_threshold: JsonEarlyResolutionThreshold, - execution_hash: aptos_rest_client::aptos_api_types::HashValue, - expiration_secs: U64, - is_resolved: bool, - min_vote_threshold: U128, - no_votes: U128, - resolution_time_secs: U64, - yes_votes: U128, - proposer: Address, - metadata: JsonMetadata, -} - -#[derive(Serialize, Deserialize, Debug)] -struct JsonEarlyResolutionThreshold { - vec: Vec, -} - -#[derive(Serialize, Deserialize, Debug)] -struct JsonMetadata { - data: Vec, -} - -#[derive(Serialize, Deserialize, Debug)] -struct JsonMetadataPair { - key: String, - value: HexEncodedBytes, -} diff --git a/m1/m1-cli/src/lib.rs b/m1/m1-cli/src/lib.rs deleted file mode 100644 index b8475e281..000000000 --- a/m1/m1-cli/src/lib.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -#![deny(unsafe_code)] - -pub mod account; -pub mod common; -pub mod config; -pub mod ffi; -pub mod genesis; -pub mod governance; -pub mod move_tool; -pub mod node; -pub mod op; -pub mod stake; -#[cfg(any(test, feature = "fuzzing"))] -pub mod test; -pub mod update; -pub mod faucet; - -use crate::common::{ - types::{CliCommand, CliResult, CliTypedResult}, - utils::cli_build_information, -}; -use async_trait::async_trait; -use clap::Parser; -use std::collections::BTreeMap; - -/// Command Line Interface (CLI) for developing and interacting with the Aptos blockchain -#[derive(Parser)] -#[clap(name = "aptos", author, version, propagate_version = true)] -pub enum Tool { - #[clap(subcommand)] - Account(account::AccountTool), - #[clap(subcommand)] - Config(config::ConfigTool), - #[clap(subcommand)] - Genesis(genesis::GenesisTool), - #[clap(subcommand)] - Governance(governance::GovernanceTool), - Info(InfoTool), - Init(common::init::InitTool), - #[clap(subcommand)] - Key(op::key::KeyTool), - #[clap(subcommand)] - Move(move_tool::MoveTool), - #[clap(subcommand)] - Multisig(account::MultisigAccountTool), - #[clap(subcommand)] - Node(node::NodeTool), - #[clap(subcommand)] - Stake(stake::StakeTool), - Update(update::UpdateTool), - Faucet(faucet::FaucetTool), -} - -impl Tool { - pub async fn execute(self) -> CliResult { - use Tool::*; - match self { - Account(tool) => tool.execute().await, - Config(tool) => tool.execute().await, - Genesis(tool) => tool.execute().await, - Governance(tool) => tool.execute().await, - Info(tool) => tool.execute_serialized().await, - // TODO: Replace entirely with config init - Init(tool) => tool.execute_serialized_success().await, - Key(tool) => tool.execute().await, - Move(tool) => tool.execute().await, - Multisig(tool) => tool.execute().await, - Node(tool) => tool.execute().await, - Stake(tool) => tool.execute().await, - Update(tool) => tool.execute_serialized().await, - Faucet(tool) => tool.execute_serialized().await, - } - } -} - -/// Show build information about the CLI -/// -/// This is useful for debugging as well as determining what versions are compatible with the CLI -#[derive(Parser)] -pub struct InfoTool {} - -#[async_trait] -impl CliCommand> for InfoTool { - fn command_name(&self) -> &'static str { - "GetCLIInfo" - } - - async fn execute(self) -> CliTypedResult> { - Ok(cli_build_information()) - } -} diff --git a/m1/m1-cli/src/main.rs b/m1/m1-cli/src/main.rs deleted file mode 100644 index 4fff4f71d..000000000 --- a/m1/m1-cli/src/main.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -//! Aptos is a one stop tool for operations, debugging, and other operations with the blockchain - -#![forbid(unsafe_code)] - -#[cfg(unix)] -#[global_allocator] -static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; - -pub use movement::{move_tool, Tool}; -use clap::Parser; -use std::process::exit; - -#[tokio::main] -async fn main() { - // Register hooks - move_tool::register_package_hooks(); - // Run the corresponding tools - let result = Tool::parse().execute().await; - - // At this point, we'll want to print and determine whether to exit for an error code - match result { - Ok(inner) => println!("{}", inner), - Err(inner) => { - println!("{}", inner); - exit(1); - }, - } -} diff --git a/m1/m1-cli/src/move_tool/aptos_debug_natives.rs b/m1/m1-cli/src/move_tool/aptos_debug_natives.rs deleted file mode 100644 index 7abcb46f7..000000000 --- a/m1/m1-cli/src/move_tool/aptos_debug_natives.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_gas::{AbstractValueSizeGasParameters, NativeGasParameters, LATEST_GAS_FEATURE_VERSION}; -use aptos_types::on_chain_config::{Features, TimedFeatures}; -use aptos_vm::natives; -use move_vm_runtime::native_functions::NativeFunctionTable; -use std::sync::Arc; - -// move_stdlib has the testing feature enabled to include debug native functions -pub fn aptos_debug_natives( - gas_parameters: NativeGasParameters, - abs_val_size_gas_params: AbstractValueSizeGasParameters, -) -> NativeFunctionTable { - // As a side effect, also configure for unit testing - natives::configure_for_unit_test(); - // Return all natives -- build with the 'testing' feature, therefore containing - // debug related functions. - natives::aptos_natives( - gas_parameters, - abs_val_size_gas_params, - LATEST_GAS_FEATURE_VERSION, - TimedFeatures::enable_all(), - Arc::new(Features::default()), - ) -} diff --git a/m1/m1-cli/src/move_tool/aptos_dep_example/README.md b/m1/m1-cli/src/move_tool/aptos_dep_example/README.md deleted file mode 100644 index abfa0f995..000000000 --- a/m1/m1-cli/src/move_tool/aptos_dep_example/README.md +++ /dev/null @@ -1,24 +0,0 @@ -This is a small example of using the new `aptos` dependency. This shall be removed once we have -documentation/tests. - -`pack2` contains a package which is used by `pack1` as follows: - -``` -[dependencies] -Pack2 = { aptos = "http://localhost:8080", address = "default" } -``` - -To see it working: - -```shell -# Start a node with an account -aptos node run-local-testnet --with-faucet & -aptos account create --account default --use-faucet -# Compile and publish pack2 -cd pack2 -aptos move compile --named-addresses project=default -aptos move publish --named-addresses project=default -# Compile pack1 agains the published pack2 -cd ../pack1 -aptos move compile --named-addresses project=default -``` \ No newline at end of file diff --git a/m1/m1-cli/src/move_tool/aptos_dep_example/pack1/Move.toml b/m1/m1-cli/src/move_tool/aptos_dep_example/pack1/Move.toml deleted file mode 100644 index dab5d3fe7..000000000 --- a/m1/m1-cli/src/move_tool/aptos_dep_example/pack1/Move.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "Pack1" -version = "0.0.0" - -[dependencies] -Pack2 = { aptos = "http://localhost:8080", address = "default" } diff --git a/m1/m1-cli/src/move_tool/aptos_dep_example/pack1/sources/hello.move b/m1/m1-cli/src/move_tool/aptos_dep_example/pack1/sources/hello.move deleted file mode 100644 index fe091952d..000000000 --- a/m1/m1-cli/src/move_tool/aptos_dep_example/pack1/sources/hello.move +++ /dev/null @@ -1,7 +0,0 @@ -module project::test { - use project::m; - - public entry fun test(_sender: &signer) { - assert!(m::add(1, 2) == 1 + 2, 1); - } -} diff --git a/m1/m1-cli/src/move_tool/aptos_dep_example/pack2/Move.toml b/m1/m1-cli/src/move_tool/aptos_dep_example/pack2/Move.toml deleted file mode 100644 index c5e6c7d00..000000000 --- a/m1/m1-cli/src/move_tool/aptos_dep_example/pack2/Move.toml +++ /dev/null @@ -1,3 +0,0 @@ -[package] -name = "Pack2" -version = "0.0.0" diff --git a/m1/m1-cli/src/move_tool/aptos_dep_example/pack2/sources/m.move b/m1/m1-cli/src/move_tool/aptos_dep_example/pack2/sources/m.move deleted file mode 100644 index ade22fadb..000000000 --- a/m1/m1-cli/src/move_tool/aptos_dep_example/pack2/sources/m.move +++ /dev/null @@ -1,3 +0,0 @@ -module project::m { - public fun add(x: u64, y: u64): u64 { x + y } -} diff --git a/m1/m1-cli/src/move_tool/coverage.rs b/m1/m1-cli/src/move_tool/coverage.rs deleted file mode 100644 index 78ffdb021..000000000 --- a/m1/m1-cli/src/move_tool/coverage.rs +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::types::{CliCommand, CliError, CliResult, CliTypedResult, MovePackageDir}; -use async_trait::async_trait; -use clap::{Parser, Subcommand}; -use move_compiler::compiled_unit::{CompiledUnit, NamedCompiledModule}; -use move_coverage::{ - coverage_map::CoverageMap, format_csv_summary, format_human_summary, - source_coverage::SourceCoverageBuilder, summary::summarize_inst_cov, -}; -use move_disassembler::disassembler::Disassembler; -use move_package::{compilation::compiled_package::CompiledPackage, BuildConfig}; - -/// Display a coverage summary for all modules in a package -/// -#[derive(Debug, Parser)] -pub struct SummaryCoverage { - /// Display function coverage summaries - /// - /// When provided, it will include coverage on a function level - #[clap(long)] - pub summarize_functions: bool, - /// Output CSV data of coverage - #[clap(long = "csv")] - pub output_csv: bool, - /// A filter string to determine which unit tests to compute coverage on - #[clap(long, short)] - pub filter: Option, - #[clap(flatten)] - pub move_options: MovePackageDir, -} - -impl SummaryCoverage { - pub fn coverage(self) -> CliTypedResult<()> { - let (coverage_map, package) = compile_coverage(self.move_options)?; - let modules: Vec<_> = package - .root_modules() - .filter_map(|unit| { - let mut retain = true; - if let Some(filter_str) = &self.filter { - if !&unit.unit.name().as_str().contains(filter_str.as_str()) { - retain = false; - } - } - match &unit.unit { - CompiledUnit::Module(NamedCompiledModule { module, .. }) if retain => { - Some(module.clone()) - }, - _ => None, - } - }) - .collect(); - let coverage_map = coverage_map.to_unified_exec_map(); - if self.output_csv { - format_csv_summary( - modules.as_slice(), - &coverage_map, - summarize_inst_cov, - &mut std::io::stdout(), - ) - } else { - format_human_summary( - modules.as_slice(), - &coverage_map, - summarize_inst_cov, - &mut std::io::stdout(), - self.summarize_functions, - ) - } - Ok(()) - } -} - -#[async_trait] -impl CliCommand<()> for SummaryCoverage { - fn command_name(&self) -> &'static str { - "SummaryCoverage" - } - - async fn execute(self) -> CliTypedResult<()> { - self.coverage() - } -} - -/// Display coverage information about the module against source code -#[derive(Debug, Parser)] -pub struct SourceCoverage { - #[clap(long = "module")] - pub module_name: String, - #[clap(flatten)] - pub move_options: MovePackageDir, -} - -#[async_trait] -impl CliCommand<()> for SourceCoverage { - fn command_name(&self) -> &'static str { - "SourceCoverage" - } - - async fn execute(self) -> CliTypedResult<()> { - let (coverage_map, package) = compile_coverage(self.move_options)?; - let unit = package.get_module_by_name_from_root(&self.module_name)?; - let source_path = &unit.source_path; - let (module, source_map) = match &unit.unit { - CompiledUnit::Module(NamedCompiledModule { - module, source_map, .. - }) => (module, source_map), - _ => panic!("Should all be modules"), - }; - let source_coverage = SourceCoverageBuilder::new(module, &coverage_map, source_map); - source_coverage - .compute_source_coverage(source_path) - .output_source_coverage(&mut std::io::stdout()) - .map_err(|err| CliError::UnexpectedError(format!("Failed to get coverage {}", err))) - } -} - -/// Display coverage information about the module against disassembled bytecode -#[derive(Debug, Parser)] -pub struct BytecodeCoverage { - #[clap(long = "module")] - pub module_name: String, - #[clap(flatten)] - pub move_options: MovePackageDir, -} - -#[async_trait] -impl CliCommand<()> for BytecodeCoverage { - fn command_name(&self) -> &'static str { - "BytecodeCoverage" - } - - async fn execute(self) -> CliTypedResult<()> { - let (coverage_map, package) = compile_coverage(self.move_options)?; - let unit = package.get_module_by_name_from_root(&self.module_name)?; - let mut disassembler = Disassembler::from_unit(&unit.unit); - disassembler.add_coverage_map(coverage_map.to_unified_exec_map()); - println!("{}", disassembler.disassemble()?); - Ok(()) - } -} - -fn compile_coverage( - move_options: MovePackageDir, -) -> CliTypedResult<(CoverageMap, CompiledPackage)> { - let config = BuildConfig { - additional_named_addresses: move_options.named_addresses(), - test_mode: false, - install_dir: move_options.output_dir.clone(), - ..Default::default() - }; - let path = move_options.get_package_path()?; - let coverage_map = - CoverageMap::from_binary_file(path.join(".coverage_map.mvcov")).map_err(|err| { - CliError::UnexpectedError(format!("Failed to retrieve coverage map {}", err)) - })?; - let package = config - .compile_package(path.as_path(), &mut Vec::new()) - .map_err(|err| CliError::MoveCompilationError(err.to_string()))?; - - Ok((coverage_map, package)) -} - -/// Computes coverage for a package -/// -/// Computes coverage on a previous unit test run for a package. Coverage input must -/// first be built with `aptos move test --coverage` -#[derive(Subcommand)] -pub enum CoveragePackage { - Summary(SummaryCoverage), - Source(SourceCoverage), - Bytecode(BytecodeCoverage), -} - -impl CoveragePackage { - pub async fn execute(self) -> CliResult { - match self { - Self::Summary(tool) => tool.execute_serialized_success().await, - Self::Source(tool) => tool.execute_serialized_success().await, - Self::Bytecode(tool) => tool.execute_serialized_success().await, - } - } -} diff --git a/m1/m1-cli/src/move_tool/disassembler.rs b/m1/m1-cli/src/move_tool/disassembler.rs deleted file mode 100644 index 12e7fa625..000000000 --- a/m1/m1-cli/src/move_tool/disassembler.rs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::{ - types::{CliCommand, CliError, CliTypedResult, PromptOptions}, - utils::{ - check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, read_from_file, - write_to_user_only_file, - }, -}; -use anyhow::Context; -use async_trait::async_trait; -use clap::Parser; -use move_binary_format::{ - binary_views::BinaryIndexedView, file_format::CompiledScript, CompiledModule, -}; -use move_bytecode_source_map::{mapping::SourceMapping, utils::source_map_from_file}; -use move_command_line_common::files::{ - MOVE_COMPILED_EXTENSION, MOVE_EXTENSION, SOURCE_MAP_EXTENSION, -}; -use move_coverage::coverage_map::CoverageMap; -use move_disassembler::disassembler::{Disassembler, DisassemblerOptions}; -use move_ir_types::location::Spanned; -use std::{fs, path::PathBuf}; - -const DISASSEMBLED_CODE_FILE: &str = "disassembled-code.yaml"; - -/// Disassemble the Move bytecode pointed to -/// -/// For example, if you want to disassemble on chain module: -/// 1. Download the package - aptos move download -/// 2. Compile the package - aptos move compile -/// 3. Cd to package and disassemble - aptos move disassemble --bytecode-path ./test.mv -#[derive(Debug, Parser)] -pub struct Disassemble { - /// Treat input file as a script (default is to treat file as a module) - #[clap(long)] - pub is_script: bool, - - /// The path to the bytecode file to disassemble; - /// - /// let's call it file.mv. We assume that two other files reside under the same directory: - /// a source map file.mvsm (possibly) and the Move source code file.move. - #[clap(long)] - pub bytecode_path: PathBuf, - - /// (Optional) Path to a coverage file for the VM in order to print trace information in the - /// disassembled output. - #[clap(long)] - pub code_coverage_path: Option, - - /// Output directory for the key files - #[clap(long, parse(from_os_str))] - pub(crate) output_dir: Option, - - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, -} - -#[async_trait] -impl CliCommand for Disassemble { - fn command_name(&self) -> &'static str { - "Disassemble" - } - - async fn execute(self) -> CliTypedResult { - let bytecode_path = self.bytecode_path.as_path(); - let extension = bytecode_path - .extension() - .context("Missing file extension for bytecode file")?; - if extension != MOVE_COMPILED_EXTENSION { - return Err(CliError::UnexpectedError(format!( - "Bad source file extension {:?}; expected {}", - extension, MOVE_COMPILED_EXTENSION - ))); - } - - let bytecode_bytes = read_from_file(bytecode_path)?; - let move_path = bytecode_path.with_extension(MOVE_EXTENSION); - let source_map_path = bytecode_path.with_extension(SOURCE_MAP_EXTENSION); - - let source = fs::read_to_string(move_path).ok(); - let source_map = source_map_from_file(&source_map_path); - - let disassembler_options = DisassemblerOptions { - print_code: true, - only_externally_visible: false, - print_basic_blocks: true, - print_locals: true, - }; - - let no_loc = Spanned::unsafe_no_loc(()).loc; - let module: CompiledModule; - let script: CompiledScript; - let bytecode = if self.is_script { - script = CompiledScript::deserialize(&bytecode_bytes) - .context("Script blob can't be deserialized")?; - BinaryIndexedView::Script(&script) - } else { - module = CompiledModule::deserialize(&bytecode_bytes) - .context("Module blob can't be deserialized")?; - BinaryIndexedView::Module(&module) - }; - - let mut source_mapping = if let Ok(s) = source_map { - SourceMapping::new(s, bytecode) - } else { - SourceMapping::new_from_view(bytecode, no_loc) - .context("Unable to build dummy source mapping")? - }; - - if let Some(source_code) = source { - source_mapping.with_source_code((bytecode_path.display().to_string(), source_code)); - } - - let mut disassembler = Disassembler::new(source_mapping, disassembler_options); - - if let Some(file_path) = &self.code_coverage_path { - disassembler.add_coverage_map( - CoverageMap::from_binary_file(file_path) - .map_err(|_err| { - CliError::UnexpectedError("Unable to read from file_path".to_string()) - })? - .to_unified_exec_map(), - ); - } - - let disassemble_string = disassembler - .disassemble() - .map_err(|_err| CliError::UnexpectedError("Unable to disassemble".to_string()))?; - - let output_dir = dir_default_to_current(self.output_dir.clone())?; - let disassemble_file = output_dir.join(DISASSEMBLED_CODE_FILE); - check_if_file_exists(disassemble_file.as_path(), self.prompt_options)?; - - // Create the directory if it doesn't exist - create_dir_if_not_exist(output_dir.as_path())?; - - // write to file - write_to_user_only_file( - disassemble_file.as_path(), - DISASSEMBLED_CODE_FILE, - disassemble_string.as_bytes(), - )?; - - Ok(disassemble_file.as_path().display().to_string()) - } -} diff --git a/m1/m1-cli/src/move_tool/manifest.rs b/m1/m1-cli/src/move_tool/manifest.rs deleted file mode 100644 index dbdcbc3e4..000000000 --- a/m1/m1-cli/src/move_tool/manifest.rs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::types::load_manifest_account_arg; -use aptos_types::account_address::AccountAddress; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::collections::BTreeMap; - -/// A Rust representation of the Move package manifest -/// -/// Note: The original Move package manifest object used by the package system -/// can't be serialized because it uses a symbol mapping -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MovePackageManifest { - pub package: PackageInfo, - #[serde(skip_serializing_if = "BTreeMap::is_empty")] - pub addresses: BTreeMap, - #[serde(skip_serializing_if = "BTreeMap::is_empty")] - pub dependencies: BTreeMap, -} - -/// Representation of an option address so we can print it as "_" -#[derive(Debug, Clone)] -pub struct ManifestNamedAddress { - pub address: Option, -} - -impl From> for ManifestNamedAddress { - fn from(opt: Option) -> Self { - ManifestNamedAddress { address: opt } - } -} - -impl From for Option { - fn from(addr: ManifestNamedAddress) -> Self { - addr.address - } -} - -impl Serialize for ManifestNamedAddress { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - if let Some(address) = self.address { - serializer.serialize_str(&address.to_hex_literal()) - } else { - serializer.serialize_str("_") - } - } -} - -impl<'de> Deserialize<'de> for ManifestNamedAddress { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let str = ::deserialize(deserializer)?; - Ok(ManifestNamedAddress { - // TODO: Cleanup unwrap - address: load_manifest_account_arg(&str).unwrap(), - }) - } -} - -/// A Rust representation of a move dependency -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Dependency { - #[serde(skip_serializing_if = "Option::is_none")] - pub local: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub git: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub rev: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub subdir: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub aptos: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub address: Option, -} - -/// A Rust representation of the package info -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PackageInfo { - pub name: String, - pub version: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub author: Option, -} diff --git a/m1/m1-cli/src/move_tool/mod.rs b/m1/m1-cli/src/move_tool/mod.rs deleted file mode 100644 index 560d0f18a..000000000 --- a/m1/m1-cli/src/move_tool/mod.rs +++ /dev/null @@ -1,1606 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -mod aptos_debug_natives; -pub mod coverage; -mod disassembler; -mod manifest; -pub mod package_hooks; -mod show; -pub mod stored_package; -mod transactional_tests_runner; - -use crate::{ - account::derive_resource_account::ResourceAccountSeed, - common::{ - types::{ - load_account_arg, ArgWithTypeVec, CliConfig, CliError, CliTypedResult, - ConfigSearchMode, EntryFunctionArguments, MoveManifestAccountWrapper, MovePackageDir, - ProfileOptions, PromptOptions, RestOptions, TransactionOptions, TransactionSummary, - }, - utils::{ - check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, - profile_or_submit, prompt_yes_with_override, write_to_file, - }, - }, - governance::CompileScriptFunction, - move_tool::{ - coverage::SummaryCoverage, - disassembler::Disassemble, - manifest::{Dependency, ManifestNamedAddress, MovePackageManifest, PackageInfo}, - }, - CliCommand, CliResult, -}; -use aptos_crypto::HashValue; -use aptos_framework::{ - build_model, docgen::DocgenOptions, extended_checks, natives::code::UpgradePolicy, - prover::ProverOptions, BuildOptions, BuiltPackage, -}; -use aptos_gas::{AbstractValueSizeGasParameters, NativeGasParameters}; -use aptos_rest_client::aptos_api_types::{EntryFunctionId, MoveType, ViewRequest}; -use aptos_transactional_test_harness::run_aptos_test; -use aptos_types::{ - account_address::{create_resource_address, AccountAddress}, - transaction::{Script, TransactionArgument, TransactionPayload}, -}; -use async_trait::async_trait; -use clap::{ArgEnum, Parser, Subcommand}; -use codespan_reporting::{ - diagnostic::Severity, - term::termcolor::{ColorChoice, StandardStream}, -}; -use itertools::Itertools; -use move_cli::{self, base::test::UnitTestResult}; -use move_command_line_common::env::MOVE_HOME; -use move_core_types::{ - identifier::Identifier, - language_storage::{ModuleId, TypeTag}, - u256::U256, -}; -use move_package::{source_package::layout::SourcePackageLayout, BuildConfig}; -use move_unit_test::UnitTestingConfig; -pub use package_hooks::*; -use serde::{Deserialize, Serialize}; -use std::{ - collections::BTreeMap, - convert::TryFrom, - fmt::{Display, Formatter}, - path::{Path, PathBuf}, - str::FromStr, -}; -pub use stored_package::*; -use tokio::task; -use transactional_tests_runner::TransactionalTestOpts; - -/// Tool for Move related operations -/// -/// This tool lets you compile, test, and publish Move code, in addition -/// to run any other tools that help run, verify, or provide information -/// about this code. -#[derive(Subcommand)] -pub enum MoveTool { - Clean(CleanPackage), - Compile(CompilePackage), - CompileScript(CompileScript), - #[clap(subcommand)] - Coverage(coverage::CoveragePackage), - CreateResourceAccountAndPublishPackage(CreateResourceAccountAndPublishPackage), - Disassemble(Disassemble), - Document(DocumentPackage), - Download(DownloadPackage), - Init(InitPackage), - List(ListPackage), - Prove(ProvePackage), - Publish(PublishPackage), - Run(RunFunction), - RunScript(RunScript), - #[clap(subcommand, hide = true)] - Show(show::ShowTool), - Test(TestPackage), - TransactionalTest(TransactionalTestOpts), - VerifyPackage(VerifyPackage), - View(ViewFunction), -} - -impl MoveTool { - pub async fn execute(self) -> CliResult { - match self { - MoveTool::Clean(tool) => tool.execute_serialized().await, - MoveTool::Compile(tool) => tool.execute_serialized().await, - MoveTool::CompileScript(tool) => tool.execute_serialized().await, - MoveTool::Coverage(tool) => tool.execute().await, - MoveTool::CreateResourceAccountAndPublishPackage(tool) => { - tool.execute_serialized_success().await - }, - MoveTool::Disassemble(tool) => tool.execute_serialized().await, - MoveTool::Document(tool) => tool.execute_serialized().await, - MoveTool::Download(tool) => tool.execute_serialized().await, - MoveTool::Init(tool) => tool.execute_serialized_success().await, - MoveTool::List(tool) => tool.execute_serialized().await, - MoveTool::Prove(tool) => tool.execute_serialized().await, - MoveTool::Publish(tool) => tool.execute_serialized().await, - MoveTool::Run(tool) => tool.execute_serialized().await, - MoveTool::RunScript(tool) => tool.execute_serialized().await, - MoveTool::Show(tool) => tool.execute_serialized().await, - MoveTool::Test(tool) => tool.execute_serialized().await, - MoveTool::TransactionalTest(tool) => tool.execute_serialized_success().await, - MoveTool::VerifyPackage(tool) => tool.execute_serialized().await, - MoveTool::View(tool) => tool.execute_serialized().await, - } - } -} - -#[derive(Parser)] -pub struct FrameworkPackageArgs { - /// Git revision or branch for the Aptos framework - /// - /// This is mutually exclusive with `--framework-local-dir` - #[clap(long, group = "framework_package_args")] - pub(crate) framework_git_rev: Option, - - /// Local framework directory for the Aptos framework - /// - /// This is mutually exclusive with `--framework-git-rev` - #[clap(long, parse(from_os_str), group = "framework_package_args")] - pub(crate) framework_local_dir: Option, - - /// Skip pulling the latest git dependencies - /// - /// If you don't have a network connection, the compiler may fail due - /// to no ability to pull git dependencies. This will allow overriding - /// this for local development. - #[clap(long)] - pub(crate) skip_fetch_latest_git_deps: bool, -} - -impl FrameworkPackageArgs { - pub fn init_move_dir( - &self, - package_dir: &Path, - name: &str, - addresses: BTreeMap, - prompt_options: PromptOptions, - ) -> CliTypedResult<()> { - const APTOS_FRAMEWORK: &str = "AptosFramework"; - const APTOS_GIT_PATH: &str = "https://github.com/aptos-labs/aptos-core.git"; - const SUBDIR_PATH: &str = "aptos-move/framework/aptos-framework"; - const DEFAULT_BRANCH: &str = "main"; - - let move_toml = package_dir.join(SourcePackageLayout::Manifest.path()); - check_if_file_exists(move_toml.as_path(), prompt_options)?; - create_dir_if_not_exist( - package_dir - .join(SourcePackageLayout::Sources.path()) - .as_path(), - )?; - - // Add the framework dependency if it's provided - let mut dependencies = BTreeMap::new(); - if let Some(ref path) = self.framework_local_dir { - dependencies.insert(APTOS_FRAMEWORK.to_string(), Dependency { - local: Some(path.display().to_string()), - git: None, - rev: None, - subdir: None, - aptos: None, - address: None, - }); - } else { - let git_rev = self.framework_git_rev.as_deref().unwrap_or(DEFAULT_BRANCH); - dependencies.insert(APTOS_FRAMEWORK.to_string(), Dependency { - local: None, - git: Some(APTOS_GIT_PATH.to_string()), - rev: Some(git_rev.to_string()), - subdir: Some(SUBDIR_PATH.to_string()), - aptos: None, - address: None, - }); - } - - let manifest = MovePackageManifest { - package: PackageInfo { - name: name.to_string(), - version: "1.0.0".to_string(), - author: None, - }, - addresses, - dependencies, - }; - - write_to_file( - move_toml.as_path(), - SourcePackageLayout::Manifest.location_str(), - toml::to_string_pretty(&manifest) - .map_err(|err| CliError::UnexpectedError(err.to_string()))? - .as_bytes(), - ) - } -} - -/// Creates a new Move package at the given location -/// -/// This will create a directory for a Move package and a corresponding -/// `Move.toml` file. -#[derive(Parser)] -pub struct InitPackage { - /// Name of the new Move package - #[clap(long)] - pub(crate) name: String, - - /// Directory to create the new Move package - #[clap(long, parse(from_os_str))] - pub(crate) package_dir: Option, - - /// Named addresses for the move binary - /// - /// Allows for an address to be put into the Move.toml, or a placeholder `_` - /// - /// Example: alice=0x1234,bob=0x5678,greg=_ - /// - /// Note: This will fail if there are duplicates in the Move.toml file remove those first. - #[clap(long, parse(try_from_str = crate::common::utils::parse_map), default_value = "")] - pub(crate) named_addresses: BTreeMap, - - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, - - #[clap(flatten)] - pub(crate) framework_package_args: FrameworkPackageArgs, -} - -#[async_trait] -impl CliCommand<()> for InitPackage { - fn command_name(&self) -> &'static str { - "InitPackage" - } - - async fn execute(self) -> CliTypedResult<()> { - let package_dir = dir_default_to_current(self.package_dir.clone())?; - let addresses = self - .named_addresses - .into_iter() - .map(|(key, value)| (key, value.account_address.into())) - .collect(); - - self.framework_package_args.init_move_dir( - package_dir.as_path(), - &self.name, - addresses, - self.prompt_options, - ) - } -} - -/// Compiles a package and returns the associated ModuleIds -#[derive(Parser)] -pub struct CompilePackage { - /// Save the package metadata in the package's build directory - /// - /// If set, package metadata should be generated and stored in the package's build directory. - /// This metadata can be used to construct a transaction to publish a package. - #[clap(long)] - pub(crate) save_metadata: bool, - - #[clap(flatten)] - pub(crate) included_artifacts_args: IncludedArtifactsArgs, - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, -} - -#[async_trait] -impl CliCommand> for CompilePackage { - fn command_name(&self) -> &'static str { - "CompilePackage" - } - - async fn execute(self) -> CliTypedResult> { - let build_options = BuildOptions { - install_dir: self.move_options.output_dir.clone(), - ..self - .included_artifacts_args - .included_artifacts - .build_options( - self.move_options.skip_fetch_latest_git_deps, - self.move_options.named_addresses(), - self.move_options.bytecode_version, - ) - }; - let pack = BuiltPackage::build(self.move_options.get_package_path()?, build_options) - .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?; - if self.save_metadata { - pack.extract_metadata_and_save()?; - } - let ids = pack - .modules() - .into_iter() - .map(|m| m.self_id().to_string()) - .collect::>(); - Ok(ids) - } -} - -/// Compiles a Move script into bytecode -/// -/// Compiles a script into bytecode and provides a hash of the bytecode. -/// This can then be run with `aptos move run-script` -#[derive(Parser)] -pub struct CompileScript { - #[clap(long, parse(from_os_str))] - pub output_file: Option, - #[clap(flatten)] - pub move_options: MovePackageDir, -} - -#[async_trait] -impl CliCommand for CompileScript { - fn command_name(&self) -> &'static str { - "CompileScript" - } - - async fn execute(self) -> CliTypedResult { - let (bytecode, script_hash) = self.compile_script().await?; - let script_location = self.output_file.unwrap_or_else(|| { - self.move_options - .get_package_path() - .unwrap() - .join("script.mv") - }); - write_to_file(script_location.as_path(), "Script", bytecode.as_slice())?; - Ok(CompileScriptOutput { - script_location, - script_hash, - }) - } -} - -impl CompileScript { - async fn compile_script(&self) -> CliTypedResult<(Vec, HashValue)> { - let build_options = BuildOptions { - install_dir: self.move_options.output_dir.clone(), - ..IncludedArtifacts::None.build_options( - self.move_options.skip_fetch_latest_git_deps, - self.move_options.named_addresses(), - self.move_options.bytecode_version, - ) - }; - let package_dir = self.move_options.get_package_path()?; - let pack = BuiltPackage::build(package_dir, build_options) - .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?; - - let scripts_count = pack.script_count(); - if scripts_count != 1 { - return Err(CliError::UnexpectedError(format!( - "Only one script can be prepared a time. Make sure one and only one script file \ - is included in the Move package. Found {} scripts.", - scripts_count - ))); - } - - let bytecode = pack.extract_script_code().pop().unwrap(); - let script_hash = HashValue::sha3_256_of(bytecode.as_slice()); - Ok((bytecode, script_hash)) - } -} - -#[derive(Debug, Serialize)] -pub struct CompileScriptOutput { - pub script_location: PathBuf, - pub script_hash: HashValue, -} - -/// Runs Move unit tests for a package -/// -/// This will run Move unit tests against a package with debug mode -/// turned on. Note, that move code warnings currently block tests from running. -#[derive(Parser)] -pub struct TestPackage { - /// A filter string to determine which unit tests to run - #[clap(long, short)] - pub filter: Option, - - /// A boolean value to skip warnings. - #[clap(long)] - pub ignore_compile_warnings: bool, - - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, - - /// The maximum number of instructions that can be executed by a test - /// - /// If set, the number of instructions executed by one test will be bounded - // TODO: Remove short, it's against the style guidelines, and update the name here - #[clap( - name = "instructions", - default_value = "100000", - short = 'i', - long = "instructions" - )] - pub instruction_execution_bound: u64, - - /// Collect coverage information for later use with the various `aptos move coverage` subcommands - #[clap(long = "coverage")] - pub compute_coverage: bool, - - /// Dump storage state on failure. - #[clap(long = "dump")] - pub dump_state: bool, -} - -#[async_trait] -impl CliCommand<&'static str> for TestPackage { - fn command_name(&self) -> &'static str { - "TestPackage" - } - - async fn execute(self) -> CliTypedResult<&'static str> { - let mut config = BuildConfig { - additional_named_addresses: self.move_options.named_addresses(), - test_mode: true, - install_dir: self.move_options.output_dir.clone(), - skip_fetch_latest_git_deps: self.move_options.skip_fetch_latest_git_deps, - ..Default::default() - }; - - // Build the Move model for extended checks - let model = &build_model( - self.move_options.get_package_path()?.as_path(), - self.move_options.named_addresses(), - None, - self.move_options.bytecode_version, - )?; - let _ = extended_checks::run_extended_checks(model); - if model.diag_count(Severity::Warning) > 0 { - let mut error_writer = StandardStream::stderr(ColorChoice::Auto); - model.report_diag(&mut error_writer, Severity::Warning); - if model.has_errors() { - return Err(CliError::MoveCompilationError( - "extended checks failed".to_string(), - )); - } - } - let path = self.move_options.get_package_path()?; - let result = move_cli::base::test::run_move_unit_tests( - path.as_path(), - config.clone(), - UnitTestingConfig { - filter: self.filter.clone(), - report_stacktrace_on_abort: true, - report_storage_on_error: self.dump_state, - ignore_compile_warnings: self.ignore_compile_warnings, - ..UnitTestingConfig::default_with_bound(None) - }, - // TODO(Gas): we may want to switch to non-zero costs in the future - aptos_debug_natives::aptos_debug_natives( - NativeGasParameters::zeros(), - AbstractValueSizeGasParameters::zeros(), - ), - None, - self.compute_coverage, - &mut std::io::stdout(), - ) - .map_err(|err| CliError::UnexpectedError(err.to_string()))?; - - // Print coverage summary if --coverage is set - if self.compute_coverage { - config.test_mode = false; - let summary = SummaryCoverage { - summarize_functions: false, - output_csv: false, - filter: self.filter, - move_options: self.move_options, - }; - summary.coverage()?; - - println!("Please use `movement move coverage -h` for more detailed source or bytecode test coverage of this package"); - } - - match result { - UnitTestResult::Success => Ok("Success"), - UnitTestResult::Failure => Err(CliError::MoveTestError), - } - } -} - -#[async_trait] -impl CliCommand<()> for TransactionalTestOpts { - fn command_name(&self) -> &'static str { - "TransactionalTest" - } - - async fn execute(self) -> CliTypedResult<()> { - let root_path = self.root_path.display().to_string(); - - let requirements = vec![transactional_tests_runner::Requirements::new( - run_aptos_test, - "tests".to_string(), - root_path, - self.pattern.clone(), - )]; - - transactional_tests_runner::runner(&self, &requirements) - } -} - -/// Proves a Move package -/// -/// This is a tool for formal verification of a Move package using -/// the Move prover -#[derive(Parser)] -pub struct ProvePackage { - #[clap(flatten)] - move_options: MovePackageDir, - - #[clap(flatten)] - prover_options: ProverOptions, -} - -#[async_trait] -impl CliCommand<&'static str> for ProvePackage { - fn command_name(&self) -> &'static str { - "ProvePackage" - } - - async fn execute(self) -> CliTypedResult<&'static str> { - let ProvePackage { - move_options, - prover_options, - } = self; - - let result = task::spawn_blocking(move || { - prover_options.prove( - move_options.get_package_path()?.as_path(), - move_options.named_addresses(), - move_options.bytecode_version, - ) - }) - .await - .map_err(|err| CliError::UnexpectedError(err.to_string()))?; - match result { - Ok(_) => Ok("Success"), - Err(e) => Err(CliError::MoveProverError(format!("{:#}", e))), - } - } -} - -/// Documents a Move package -/// -/// This converts the content of the package into markdown for documentation. -#[derive(Parser)] -pub struct DocumentPackage { - #[clap(flatten)] - move_options: MovePackageDir, - - #[clap(flatten)] - docgen_options: DocgenOptions, -} - -#[async_trait] -impl CliCommand<&'static str> for DocumentPackage { - fn command_name(&self) -> &'static str { - "DocumentPackage" - } - - async fn execute(self) -> CliTypedResult<&'static str> { - let DocumentPackage { - move_options, - docgen_options, - } = self; - let build_options = BuildOptions { - with_srcs: false, - with_abis: false, - with_source_maps: false, - with_error_map: false, - with_docs: true, - install_dir: None, - named_addresses: move_options.named_addresses(), - docgen_options: Some(docgen_options), - skip_fetch_latest_git_deps: move_options.skip_fetch_latest_git_deps, - bytecode_version: move_options.bytecode_version, - }; - BuiltPackage::build(move_options.get_package_path()?, build_options)?; - Ok("succeeded") - } -} - -#[derive(Parser)] -pub struct IncludedArtifactsArgs { - /// Artifacts to be generated when building the package - /// - /// Which artifacts to include in the package. This can be one of `none`, `sparse`, and - /// `all`. `none` is the most compact form and does not allow to reconstruct a source - /// package from chain; `sparse` is the minimal set of artifacts needed to reconstruct - /// a source package; `all` includes all available artifacts. The choice of included - /// artifacts heavily influences the size and therefore gas cost of publishing: `none` - /// is the size of bytecode alone; `sparse` is roughly 2 times as much; and `all` 3-4 - /// as much. - #[clap(long, default_value_t = IncludedArtifacts::Sparse)] - pub(crate) included_artifacts: IncludedArtifacts, -} - -/// Publishes the modules in a Move package to the Aptos blockchain -#[derive(Parser)] -pub struct PublishPackage { - /// Whether to override the check for maximal size of published data - #[clap(long)] - pub(crate) override_size_check: bool, - - #[clap(flatten)] - pub(crate) included_artifacts_args: IncludedArtifactsArgs, - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[derive(ArgEnum, Clone, Copy, Debug)] -pub enum IncludedArtifacts { - None, - Sparse, - All, -} - -impl Display for IncludedArtifacts { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - use IncludedArtifacts::*; - match self { - None => f.write_str("none"), - Sparse => f.write_str("sparse"), - All => f.write_str("all"), - } - } -} - -impl FromStr for IncludedArtifacts { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - use IncludedArtifacts::*; - match s { - "none" => Ok(None), - "sparse" => Ok(Sparse), - "all" => Ok(All), - _ => Err("unknown variant"), - } - } -} - -impl IncludedArtifacts { - pub(crate) fn build_options( - self, - skip_fetch_latest_git_deps: bool, - named_addresses: BTreeMap, - bytecode_version: Option, - ) -> BuildOptions { - use IncludedArtifacts::*; - match self { - None => BuildOptions { - with_srcs: false, - with_abis: false, - with_source_maps: false, - // Always enable error map bytecode injection - with_error_map: true, - named_addresses, - skip_fetch_latest_git_deps, - bytecode_version, - ..BuildOptions::default() - }, - Sparse => BuildOptions { - with_srcs: true, - with_abis: false, - with_source_maps: false, - with_error_map: true, - named_addresses, - skip_fetch_latest_git_deps, - bytecode_version, - ..BuildOptions::default() - }, - All => BuildOptions { - with_srcs: true, - with_abis: true, - with_source_maps: true, - with_error_map: true, - named_addresses, - skip_fetch_latest_git_deps, - bytecode_version, - ..BuildOptions::default() - }, - } - } -} - -pub const MAX_PUBLISH_PACKAGE_SIZE: usize = 60_000; - -#[async_trait] -impl CliCommand for PublishPackage { - fn command_name(&self) -> &'static str { - "PublishPackage" - } - - async fn execute(self) -> CliTypedResult { - let PublishPackage { - move_options, - txn_options, - override_size_check, - included_artifacts_args, - } = self; - let package_path = move_options.get_package_path()?; - let options = included_artifacts_args.included_artifacts.build_options( - move_options.skip_fetch_latest_git_deps, - move_options.named_addresses(), - move_options.bytecode_version, - ); - let package = BuiltPackage::build(package_path, options)?; - let compiled_units = package.extract_code(); - - // Send the compiled module and metadata using the code::publish_package_txn. - let metadata = package.extract_metadata()?; - let payload = aptos_cached_packages::aptos_stdlib::code_publish_package_txn( - bcs::to_bytes(&metadata).expect("PackageMetadata has BCS"), - compiled_units, - ); - let size = bcs::serialized_size(&payload)?; - println!("package size {} bytes", size); - if !override_size_check && size > MAX_PUBLISH_PACKAGE_SIZE { - return Err(CliError::UnexpectedError(format!( - "The package is larger than {} bytes ({} bytes)! To lower the size \ - you may want to include less artifacts via `--included-artifacts`. \ - You can also override this check with `--override-size-check", - MAX_PUBLISH_PACKAGE_SIZE, size - ))); - } - profile_or_submit(payload, &txn_options).await - } -} - -/// Publishes the modules in a Move package to the Aptos blockchain under a resource account -#[derive(Parser)] -pub struct CreateResourceAccountAndPublishPackage { - /// The named address for compiling and using in the contract - /// - /// This will take the derived account address for the resource account and put it in this location - #[clap(long)] - pub(crate) address_name: String, - - /// Whether to override the check for maximal size of published data - /// - /// This won't bypass on chain checks, so if you are not allowed to go over the size check, it - /// will still be blocked from publishing. - #[clap(long)] - pub(crate) override_size_check: bool, - - #[clap(flatten)] - pub(crate) seed_args: ResourceAccountSeed, - #[clap(flatten)] - pub(crate) included_artifacts_args: IncludedArtifactsArgs, - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for CreateResourceAccountAndPublishPackage { - fn command_name(&self) -> &'static str { - "ResourceAccountPublishPackage" - } - - async fn execute(self) -> CliTypedResult { - let CreateResourceAccountAndPublishPackage { - address_name, - mut move_options, - txn_options, - override_size_check, - included_artifacts_args, - seed_args, - } = self; - - let account = if let Some(Some(account)) = CliConfig::load_profile( - txn_options.profile_options.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|p| p.account) - { - account - } else { - return Err(CliError::CommandArgumentError( - "Please provide an account using --profile or run movement init".to_string(), - )); - }; - let seed = seed_args.seed()?; - - let resource_address = create_resource_address(account, &seed); - move_options.add_named_address(address_name, resource_address.to_string()); - - let package_path = move_options.get_package_path()?; - let options = included_artifacts_args.included_artifacts.build_options( - move_options.skip_fetch_latest_git_deps, - move_options.named_addresses(), - move_options.bytecode_version, - ); - let package = BuiltPackage::build(package_path, options)?; - let compiled_units = package.extract_code(); - - // Send the compiled module and metadata using the code::publish_package_txn. - let metadata = package.extract_metadata()?; - - let message = format!( - "Do you want to publish this package under the resource account's address {}?", - resource_address - ); - prompt_yes_with_override(&message, txn_options.prompt_options)?; - - let payload = aptos_cached_packages::aptos_stdlib::resource_account_create_resource_account_and_publish_package( - seed, - bcs::to_bytes(&metadata).expect("PackageMetadata has BCS"), - compiled_units, - ); - let size = bcs::serialized_size(&payload)?; - println!("package size {} bytes", size); - if !override_size_check && size > MAX_PUBLISH_PACKAGE_SIZE { - return Err(CliError::UnexpectedError(format!( - "The package is larger than {} bytes ({} bytes)! To lower the size \ - you may want to include less artifacts via `--included-artifacts`. \ - You can also override this check with `--override-size-check", - MAX_PUBLISH_PACKAGE_SIZE, size - ))); - } - txn_options - .submit_transaction(payload) - .await - .map(TransactionSummary::from) - } -} - -/// Downloads a package and stores it in a directory named after the package -/// -/// This lets you retrieve packages directly from the blockchain for inspection -/// and use as a local dependency in testing. -#[derive(Parser)] -pub struct DownloadPackage { - /// Address of the account containing the package - #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] - pub(crate) account: AccountAddress, - - /// Name of the package - #[clap(long)] - pub package: String, - - /// Directory to store downloaded package. Defaults to the current directory. - #[clap(long, parse(from_os_str))] - pub output_dir: Option, - - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, -} - -#[async_trait] -impl CliCommand<&'static str> for DownloadPackage { - fn command_name(&self) -> &'static str { - "DownloadPackage" - } - - async fn execute(self) -> CliTypedResult<&'static str> { - let url = self.rest_options.url(&self.profile_options)?; - let registry = CachedPackageRegistry::create(url, self.account).await?; - let output_dir = dir_default_to_current(self.output_dir)?; - - let package = registry - .get_package(self.package) - .await - .map_err(|s| CliError::CommandArgumentError(s.to_string()))?; - if package.upgrade_policy() == UpgradePolicy::arbitrary() { - return Err(CliError::CommandArgumentError( - "A package with upgrade policy `arbitrary` cannot be downloaded \ - since it is not safe to depend on such packages." - .to_owned(), - )); - } - let package_path = output_dir.join(package.name()); - package - .save_package_to_disk(package_path.as_path()) - .map_err(|e| CliError::UnexpectedError(format!("Failed to save package: {}", e)))?; - println!( - "Saved package with {} module(s) to `{}`", - package.module_names().len(), - package_path.display() - ); - Ok("Download succeeded") - } -} - -/// Downloads a package and verifies the bytecode -/// -/// Downloads the package from onchain and verifies the bytecode matches a local compilation of the Move code -#[derive(Parser)] -pub struct VerifyPackage { - /// Address of the account containing the package - #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] - pub(crate) account: AccountAddress, - - /// Artifacts to be generated when building this package. - #[clap(long, default_value_t = IncludedArtifacts::Sparse)] - pub(crate) included_artifacts: IncludedArtifacts, - - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, -} - -#[async_trait] -impl CliCommand<&'static str> for VerifyPackage { - fn command_name(&self) -> &'static str { - "DownloadPackage" - } - - async fn execute(self) -> CliTypedResult<&'static str> { - // First build the package locally to get the package metadata - let build_options = BuildOptions { - install_dir: self.move_options.output_dir.clone(), - bytecode_version: self.move_options.bytecode_version, - ..self.included_artifacts.build_options( - self.move_options.skip_fetch_latest_git_deps, - self.move_options.named_addresses(), - self.move_options.bytecode_version, - ) - }; - let pack = BuiltPackage::build(self.move_options.get_package_path()?, build_options) - .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?; - let compiled_metadata = pack.extract_metadata()?; - - // Now pull the compiled package - let url = self.rest_options.url(&self.profile_options)?; - let registry = CachedPackageRegistry::create(url, self.account).await?; - let package = registry - .get_package(pack.name()) - .await - .map_err(|s| CliError::CommandArgumentError(s.to_string()))?; - - // We can't check the arbitrary, because it could change on us - if package.upgrade_policy() == UpgradePolicy::arbitrary() { - return Err(CliError::CommandArgumentError( - "A package with upgrade policy `arbitrary` cannot be downloaded \ - since it is not safe to depend on such packages." - .to_owned(), - )); - } - - // Verify that the source digest matches - package.verify(&compiled_metadata)?; - - Ok("Successfully verified source of package") - } -} - -/// Lists information about packages and modules on-chain for an account -#[derive(Parser)] -pub struct ListPackage { - /// Address of the account for which to list packages. - #[clap(long, parse(try_from_str = crate::common::types::load_account_arg))] - pub(crate) account: AccountAddress, - - /// Type of items to query - /// - /// Current supported types `[packages]` - #[clap(long, default_value_t = MoveListQuery::Packages)] - query: MoveListQuery, - - #[clap(flatten)] - rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, -} - -#[derive(ArgEnum, Clone, Copy, Debug)] -pub enum MoveListQuery { - Packages, -} - -impl Display for MoveListQuery { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - MoveListQuery::Packages => "packages", - }) - } -} - -impl FromStr for MoveListQuery { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "packages" => Ok(MoveListQuery::Packages), - _ => Err("Invalid query. Valid values are modules, packages"), - } - } -} - -#[async_trait] -impl CliCommand<&'static str> for ListPackage { - fn command_name(&self) -> &'static str { - "ListPackage" - } - - async fn execute(self) -> CliTypedResult<&'static str> { - let url = self.rest_options.url(&self.profile_options)?; - let registry = CachedPackageRegistry::create(url, self.account).await?; - match self.query { - MoveListQuery::Packages => { - for name in registry.package_names() { - let data = registry.get_package(name).await?; - println!("package {}", data.name()); - println!(" upgrade_policy: {}", data.upgrade_policy()); - println!(" upgrade_number: {}", data.upgrade_number()); - println!(" source_digest: {}", data.source_digest()); - println!(" modules: {}", data.module_names().into_iter().join(", ")); - } - }, - } - Ok("list succeeded") - } -} - -/// Cleans derived artifacts of a package. -#[derive(Parser)] -pub struct CleanPackage { - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, -} - -#[async_trait] -impl CliCommand<&'static str> for CleanPackage { - fn command_name(&self) -> &'static str { - "CleanPackage" - } - - async fn execute(self) -> CliTypedResult<&'static str> { - let path = self.move_options.get_package_path()?; - let build_dir = path.join("build"); - // Only remove the build dir if it exists, allowing for users to still clean their cache - if build_dir.exists() { - std::fs::remove_dir_all(build_dir.as_path()) - .map_err(|e| CliError::IO(build_dir.display().to_string(), e))?; - } - - let move_dir = PathBuf::from(MOVE_HOME.as_str()); - if move_dir.exists() - && prompt_yes_with_override( - &format!( - "Do you also want to delete the local package download cache at `{}`?", - move_dir.display() - ), - self.prompt_options, - ) - .is_ok() - { - std::fs::remove_dir_all(move_dir.as_path()) - .map_err(|e| CliError::IO(move_dir.display().to_string(), e))?; - } - Ok("succeeded") - } -} - -/// Run a Move function -#[derive(Parser)] -pub struct RunFunction { - #[clap(flatten)] - pub(crate) entry_function_args: EntryFunctionArguments, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for RunFunction { - fn command_name(&self) -> &'static str { - "RunFunction" - } - - async fn execute(self) -> CliTypedResult { - let payload = TransactionPayload::EntryFunction( - self.entry_function_args.create_entry_function_payload()?, - ); - profile_or_submit(payload, &self.txn_options).await - } -} - -/// Run a view function -#[derive(Parser)] -pub struct ViewFunction { - #[clap(flatten)] - pub(crate) entry_function_args: EntryFunctionArguments, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand> for ViewFunction { - fn command_name(&self) -> &'static str { - "RunViewFunction" - } - - async fn execute(self) -> CliTypedResult> { - let mut args: Vec = vec![]; - for arg in self.entry_function_args.arg_vec.args { - args.push(arg.to_json()?); - } - - let view_request = ViewRequest { - function: EntryFunctionId { - module: self.entry_function_args.function_id.module_id.into(), - name: self.entry_function_args.function_id.member_id.into(), - }, - type_arguments: self.entry_function_args.type_args, - arguments: args, - }; - - self.txn_options.view(view_request).await - } -} - -/// Run a Move script -#[derive(Parser)] -pub struct RunScript { - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) compile_proposal_args: CompileScriptFunction, - #[clap(flatten)] - pub(crate) arg_vec: ArgWithTypeVec, - /// TypeTag arguments separated by spaces. - /// - /// Example: `u8 u16 u32 u64 u128 u256 bool address vector signer` - #[clap(long, multiple_values = true)] - pub(crate) type_args: Vec, -} - -#[async_trait] -impl CliCommand for RunScript { - fn command_name(&self) -> &'static str { - "RunScript" - } - - async fn execute(self) -> CliTypedResult { - let (bytecode, _script_hash) = self - .compile_proposal_args - .compile("RunScript", self.txn_options.prompt_options)?; - - let mut args: Vec = vec![]; - for arg in self.arg_vec.args { - args.push(arg.try_into()?); - } - - let mut type_args: Vec = Vec::new(); - - // These TypeArgs are used for generics - for type_arg in self.type_args.into_iter() { - let type_tag = TypeTag::try_from(type_arg) - .map_err(|err| CliError::UnableToParse("--type-args", err.to_string()))?; - type_args.push(type_tag) - } - - let payload = TransactionPayload::Script(Script::new(bytecode, type_args, args)); - - profile_or_submit(payload, &self.txn_options).await - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) enum FunctionArgType { - Address, - Bool, - Hex, - String, - U8, - U16, - U32, - U64, - U128, - U256, - Raw, -} - -impl Display for FunctionArgType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - FunctionArgType::Address => write!(f, "address"), - FunctionArgType::Bool => write!(f, "bool"), - FunctionArgType::Hex => write!(f, "hex"), - FunctionArgType::String => write!(f, "string"), - FunctionArgType::U8 => write!(f, "u8"), - FunctionArgType::U16 => write!(f, "u16"), - FunctionArgType::U32 => write!(f, "u32"), - FunctionArgType::U64 => write!(f, "u64"), - FunctionArgType::U128 => write!(f, "u128"), - FunctionArgType::U256 => write!(f, "u256"), - FunctionArgType::Raw => write!(f, "raw"), - } - } -} - -impl FunctionArgType { - /// Parse a standalone argument (not a vector) from string slice into BCS representation. - fn parse_arg_str(&self, arg: &str) -> CliTypedResult> { - match self { - FunctionArgType::Address => bcs::to_bytes( - &load_account_arg(arg) - .map_err(|err| CliError::UnableToParse("address", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::Bool => bcs::to_bytes( - &bool::from_str(arg) - .map_err(|err| CliError::UnableToParse("bool", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::Hex => bcs::to_bytes( - &hex::decode(arg).map_err(|err| CliError::UnableToParse("hex", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::String => bcs::to_bytes(arg).map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::U8 => bcs::to_bytes( - &u8::from_str(arg).map_err(|err| CliError::UnableToParse("u8", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::U16 => bcs::to_bytes( - &u16::from_str(arg) - .map_err(|err| CliError::UnableToParse("u16", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::U32 => bcs::to_bytes( - &u32::from_str(arg) - .map_err(|err| CliError::UnableToParse("u32", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::U64 => bcs::to_bytes( - &u64::from_str(arg) - .map_err(|err| CliError::UnableToParse("u64", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::U128 => bcs::to_bytes( - &u128::from_str(arg) - .map_err(|err| CliError::UnableToParse("u128", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::U256 => bcs::to_bytes( - &U256::from_str(arg) - .map_err(|err| CliError::UnableToParse("u256", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::Raw => { - hex::decode(arg).map_err(|err| CliError::UnableToParse("raw", err.to_string())) - }, - } - } - - /// Recursively parse argument JSON into BCS representation. - pub fn parse_arg_json(&self, arg: &serde_json::Value) -> CliTypedResult { - match arg { - serde_json::Value::Bool(value) => Ok(ArgWithType { - _ty: self.clone(), - _vector_depth: 0, - arg: self.parse_arg_str(value.to_string().as_str())?, - }), - serde_json::Value::Number(value) => Ok(ArgWithType { - _ty: self.clone(), - _vector_depth: 0, - arg: self.parse_arg_str(value.to_string().as_str())?, - }), - serde_json::Value::String(value) => Ok(ArgWithType { - _ty: self.clone(), - _vector_depth: 0, - arg: self.parse_arg_str(value.as_str())?, - }), - serde_json::Value::Array(_) => { - let mut bcs: Vec = vec![]; // BCS representation of argument. - let mut common_sub_arg_depth = None; - // Prepend argument sequence length to BCS bytes vector. - write_u64_as_uleb128(&mut bcs, arg.as_array().unwrap().len()); - // Loop over all of the vector's sub-arguments, which may also be vectors: - for sub_arg in arg.as_array().unwrap() { - let ArgWithType { - _ty: _, - _vector_depth: sub_arg_depth, - arg: mut sub_arg_bcs, - } = self.parse_arg_json(sub_arg)?; - // Verify all sub-arguments have same depth. - if let Some(check_depth) = common_sub_arg_depth { - if check_depth != sub_arg_depth { - return Err(CliError::CommandArgumentError( - "Variable vector depth".to_string(), - )); - } - }; - common_sub_arg_depth = Some(sub_arg_depth); - bcs.append(&mut sub_arg_bcs); // Append sub-argument BCS. - } - // Default sub-argument depth is 0 for when no sub-arguments were looped over. - Ok(ArgWithType { - _ty: self.clone(), - _vector_depth: common_sub_arg_depth.unwrap_or(0) + 1, - arg: bcs, - }) - }, - serde_json::Value::Null => { - Err(CliError::CommandArgumentError("Null argument".to_string())) - }, - serde_json::Value::Object(_) => Err(CliError::CommandArgumentError( - "JSON object argument".to_string(), - )), - } - } -} - -// TODO use from move_binary_format::file_format_common if it is made public. -fn write_u64_as_uleb128(binary: &mut Vec, mut val: usize) { - loop { - let cur = val & 0x7F; - if cur != val { - binary.push((cur | 0x80) as u8); - val >>= 7; - } else { - binary.push(cur as u8); - break; - } - } -} - -impl FromStr for FunctionArgType { - type Err = CliError; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "address" => Ok(FunctionArgType::Address), - "bool" => Ok(FunctionArgType::Bool), - "hex" => Ok(FunctionArgType::Hex), - "string" => Ok(FunctionArgType::String), - "u8" => Ok(FunctionArgType::U8), - "u16" => Ok(FunctionArgType::U16), - "u32" => Ok(FunctionArgType::U32), - "u64" => Ok(FunctionArgType::U64), - "u128" => Ok(FunctionArgType::U128), - "u256" => Ok(FunctionArgType::U256), - "raw" => Ok(FunctionArgType::Raw), - str => {Err(CliError::CommandArgumentError(format!( - "Invalid arg type '{}'. Must be one of: ['{}','{}','{}','{}','{}','{}','{}','{}','{}','{}','{}']", - str, - FunctionArgType::Address, - FunctionArgType::Bool, - FunctionArgType::Hex, - FunctionArgType::String, - FunctionArgType::U8, - FunctionArgType::U16, - FunctionArgType::U32, - FunctionArgType::U64, - FunctionArgType::U128, - FunctionArgType::U256, - FunctionArgType::Raw))) - } - } - } -} - -/// A parseable arg with a type separated by a colon -#[derive(Debug)] -pub struct ArgWithType { - pub(crate) _ty: FunctionArgType, - pub(crate) _vector_depth: u8, - pub(crate) arg: Vec, -} - -impl ArgWithType { - pub fn address(account_address: AccountAddress) -> Self { - ArgWithType { - _ty: FunctionArgType::Address, - _vector_depth: 0, - arg: bcs::to_bytes(&account_address).unwrap(), - } - } - - pub fn u64(arg: u64) -> Self { - ArgWithType { - _ty: FunctionArgType::U64, - _vector_depth: 0, - arg: bcs::to_bytes(&arg).unwrap(), - } - } - - pub fn bytes(arg: Vec) -> Self { - ArgWithType { - _ty: FunctionArgType::Raw, - _vector_depth: 0, - arg: bcs::to_bytes(&arg).unwrap(), - } - } - - pub fn raw(arg: Vec) -> Self { - ArgWithType { - _ty: FunctionArgType::Raw, - _vector_depth: 0, - arg, - } - } - - pub fn bcs_value_to_json<'a, T: Deserialize<'a> + Serialize>( - &'a self, - ) -> CliTypedResult { - match self._vector_depth { - 0 => serde_json::to_value(bcs::from_bytes::(&self.arg)?) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - 1 => serde_json::to_value(bcs::from_bytes::>(&self.arg)?) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - - 2 => serde_json::to_value(bcs::from_bytes::>>(&self.arg)?) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - - 3 => serde_json::to_value(bcs::from_bytes::>>>(&self.arg)?) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - - 4 => serde_json::to_value(bcs::from_bytes::>>>>(&self.arg)?) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - 5 => serde_json::to_value(bcs::from_bytes::>>>>>(&self.arg)?) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - 6 => serde_json::to_value(bcs::from_bytes::>>>>>>( - &self.arg, - )?) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - 7 => serde_json::to_value(bcs::from_bytes::>>>>>>>( - &self.arg, - )?) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - depth => Err(CliError::UnexpectedError(format!( - "Vector of depth {depth} is overly nested" - ))), - } - } - - pub fn to_json(&self) -> CliTypedResult { - match self._ty { - FunctionArgType::Address => self.bcs_value_to_json::(), - FunctionArgType::Bool => self.bcs_value_to_json::(), - FunctionArgType::Hex => self.bcs_value_to_json::>(), - FunctionArgType::String => self.bcs_value_to_json::(), - FunctionArgType::U8 => self.bcs_value_to_json::(), - FunctionArgType::U16 => self.bcs_value_to_json::(), - FunctionArgType::U32 => self.bcs_value_to_json::(), - FunctionArgType::U64 => self.bcs_value_to_json::(), - FunctionArgType::U128 => self.bcs_value_to_json::(), - FunctionArgType::U256 => self.bcs_value_to_json::(), - FunctionArgType::Raw => serde_json::to_value(&self.arg) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - } - .map_err(|err| { - CliError::UnexpectedError(format!("Failed to parse argument to JSON {}", err)) - }) - } -} - -/// Does not support string arguments that contain the following characters: -/// -/// * `,` -/// * `[` -/// * `]` -impl FromStr for ArgWithType { - type Err = CliError; - - fn from_str(s: &str) -> Result { - // Splits on the first colon, returning at most `2` elements - // This is required to support args that contain a colon - let parts: Vec<_> = s.splitn(2, ':').collect(); - if parts.len() != 2 { - return Err(CliError::CommandArgumentError( - "Arguments must be pairs of : e.g. bool:true".to_string(), - )); - } - let ty = FunctionArgType::from_str(parts.first().unwrap())?; - let mut arg = String::from(*parts.last().unwrap()); - // May need to surround with quotes if not an array, so arg can be parsed into JSON. - if !arg.starts_with('[') { - if let FunctionArgType::Address - | FunctionArgType::Hex - | FunctionArgType::String - | FunctionArgType::Raw = ty - { - arg = format!("\"{}\"", arg); - } - } - let json = serde_json::from_str::(arg.as_str()) - .map_err(|err| CliError::UnexpectedError(err.to_string()))?; - ty.parse_arg_json(&json) - } -} - -impl TryInto for ArgWithType { - type Error = CliError; - - fn try_into(self) -> Result { - if self._vector_depth > 0 && self._ty != FunctionArgType::U8 { - return Err(CliError::UnexpectedError( - "Unable to parse non-u8 vector to transaction argument".to_string(), - )); - } - match self._ty { - FunctionArgType::Address => Ok(TransactionArgument::Address(txn_arg_parser( - &self.arg, "address", - )?)), - FunctionArgType::Bool => Ok(TransactionArgument::Bool(txn_arg_parser( - &self.arg, "bool", - )?)), - FunctionArgType::Hex => Ok(TransactionArgument::U8Vector(txn_arg_parser( - &self.arg, "hex", - )?)), - FunctionArgType::String => Ok(TransactionArgument::U8Vector(txn_arg_parser( - &self.arg, "string", - )?)), - FunctionArgType::U8 => match self._vector_depth { - 0 => Ok(TransactionArgument::U8(txn_arg_parser(&self.arg, "u8")?)), - 1 => Ok(TransactionArgument::U8Vector(txn_arg_parser( - &self.arg, - "vector", - )?)), - depth => Err(CliError::UnexpectedError(format!( - "Unable to parse u8 vector of depth {} to transaction argument", - depth - ))), - }, - FunctionArgType::U16 => Ok(TransactionArgument::U16(txn_arg_parser(&self.arg, "u16")?)), - FunctionArgType::U32 => Ok(TransactionArgument::U32(txn_arg_parser(&self.arg, "u32")?)), - FunctionArgType::U64 => Ok(TransactionArgument::U64(txn_arg_parser(&self.arg, "u64")?)), - FunctionArgType::U128 => Ok(TransactionArgument::U128(txn_arg_parser( - &self.arg, "u128", - )?)), - FunctionArgType::U256 => Ok(TransactionArgument::U256(txn_arg_parser( - &self.arg, "u256", - )?)), - FunctionArgType::Raw => Ok(TransactionArgument::U8Vector(txn_arg_parser( - &self.arg, "raw", - )?)), - } - } -} - -fn txn_arg_parser( - data: &[u8], - label: &'static str, -) -> Result { - bcs::from_bytes(data).map_err(|err| CliError::UnableToParse(label, err.to_string())) -} - -/// Identifier of a module member (function or struct). -#[derive(Debug, Clone)] -pub struct MemberId { - pub module_id: ModuleId, - pub member_id: Identifier, -} - -fn parse_member_id(function_id: &str) -> CliTypedResult { - let ids: Vec<&str> = function_id.split_terminator("::").collect(); - if ids.len() != 3 { - return Err(CliError::CommandArgumentError( - "FunctionId is not well formed. Must be of the form
::::" - .to_string(), - )); - } - let address = load_account_arg(ids.first().unwrap())?; - let module = Identifier::from_str(ids.get(1).unwrap()) - .map_err(|err| CliError::UnableToParse("Module Name", err.to_string()))?; - let member_id = Identifier::from_str(ids.get(2).unwrap()) - .map_err(|err| CliError::UnableToParse("Member Name", err.to_string()))?; - let module_id = ModuleId::new(address, module); - Ok(MemberId { - module_id, - member_id, - }) -} - -impl FromStr for MemberId { - type Err = CliError; - - fn from_str(s: &str) -> Result { - parse_member_id(s) - } -} diff --git a/m1/m1-cli/src/move_tool/package_hooks.rs b/m1/m1-cli/src/move_tool/package_hooks.rs deleted file mode 100644 index 208d368c3..000000000 --- a/m1/m1-cli/src/move_tool/package_hooks.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{common::types::load_account_arg, move_tool::CachedPackageRegistry}; -use aptos_framework::UPGRADE_POLICY_CUSTOM_FIELD; -use futures::executor::block_on; -use move_package::{ - compilation::package_layout::CompiledPackageLayout, package_hooks::PackageHooks, - source_package::parsed_manifest::CustomDepInfo, -}; -use move_symbol_pool::Symbol; -use reqwest::Url; - -pub fn register_package_hooks() { - move_package::package_hooks::register_package_hooks(Box::new(AptosPackageHooks {})) -} - -struct AptosPackageHooks {} - -impl PackageHooks for AptosPackageHooks { - fn custom_package_info_fields(&self) -> Vec { - vec![UPGRADE_POLICY_CUSTOM_FIELD.to_string()] - } - - fn custom_dependency_key(&self) -> Option { - Some("movement".to_string()) - } - - fn resolve_custom_dependency( - &self, - _dep_name: Symbol, - info: &CustomDepInfo, - ) -> anyhow::Result<()> { - block_on(maybe_download_package(info)) - } -} - -async fn maybe_download_package(info: &CustomDepInfo) -> anyhow::Result<()> { - if !info - .download_to - .join(CompiledPackageLayout::BuildInfo.path()) - .exists() - { - let registry = CachedPackageRegistry::create( - Url::parse(info.node_url.as_str())?, - load_account_arg(info.package_address.as_str())?, - ) - .await?; - let package = registry.get_package(info.package_name).await?; - package.save_package_to_disk(info.download_to.as_path()) - } else { - Ok(()) - } -} diff --git a/m1/m1-cli/src/move_tool/show.rs b/m1/m1-cli/src/move_tool/show.rs deleted file mode 100644 index e1cc0e726..000000000 --- a/m1/m1-cli/src/move_tool/show.rs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::IncludedArtifactsArgs; -use crate::common::types::{CliCommand, CliError, CliResult, CliTypedResult, MovePackageDir}; -use anyhow::Context; -use aptos_framework::{BuildOptions, BuiltPackage}; -use aptos_types::transaction::EntryABI; -use async_trait::async_trait; -use clap::{Parser, Subcommand}; - -#[derive(Subcommand)] -pub enum ShowTool { - Abi(ShowAbi), -} - -impl ShowTool { - pub async fn execute_serialized(self) -> CliResult { - match self { - Self::Abi(tool) => tool.execute_serialized().await, - } - } -} - -/// Compile the package and show information about the ABIs of the compiled modules. -/// -/// For example, this would show the function `transfer` in the module `coin`: -/// -/// aptos move show abi --modules coin --names transfer -/// -#[derive(Parser)] -pub struct ShowAbi { - /// If provided, only show items from the given Move modules. These should be module - /// names, not file paths. For example, `coin`. - #[clap(long, multiple_values = true)] - modules: Vec, - - /// If provided, only show items with the given names. For example, `transfer`. - #[clap(long, multiple_values = true)] - names: Vec, - - #[clap(flatten)] - included_artifacts_args: IncludedArtifactsArgs, - - #[clap(flatten)] - move_options: MovePackageDir, -} - -#[async_trait] -impl CliCommand> for ShowAbi { - fn command_name(&self) -> &'static str { - "ShowAbi" - } - - async fn execute(self) -> CliTypedResult> { - let build_options = BuildOptions { - install_dir: self.move_options.output_dir.clone(), - with_abis: true, - ..self - .included_artifacts_args - .included_artifacts - .build_options( - self.move_options.skip_fetch_latest_git_deps, - self.move_options.named_addresses(), - self.move_options.bytecode_version, - ) - }; - - // Build the package. - let package = BuiltPackage::build(self.move_options.get_package_path()?, build_options) - .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?; - - // Get ABIs from the package. - let abis = package - .extract_abis() - .context("No ABIs found after compilation")?; - - // Filter the ABIs based on the filters passed in. - let abis = abis - .into_iter() - .filter(|abi| { - let name = abi.name().to_string(); - if !self.names.is_empty() && !self.names.contains(&name) { - return false; - } - match &abi { - EntryABI::EntryFunction(func) => { - if !self.modules.is_empty() - && !self - .modules - .contains(&func.module_name().name().to_string()) - { - return false; - } - }, - EntryABI::TransactionScript(_) => { - // If there were any modules specified we ignore scripts. - if !self.modules.is_empty() { - return false; - } - }, - } - true - }) - .collect(); - - Ok(abis) - } -} diff --git a/m1/m1-cli/src/move_tool/stored_package.rs b/m1/m1-cli/src/move_tool/stored_package.rs deleted file mode 100644 index c957c4c55..000000000 --- a/m1/m1-cli/src/move_tool/stored_package.rs +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::bail; -use aptos_framework::{ - natives::code::{ModuleMetadata, PackageMetadata, PackageRegistry, UpgradePolicy}, - unzip_metadata_str, -}; -use aptos_rest_client::Client; -use aptos_types::account_address::AccountAddress; -use move_package::compilation::package_layout::CompiledPackageLayout; -use reqwest::Url; -use std::{fs, path::Path}; - -// TODO: this is a first naive implementation of the package registry. Before mainnet -// we need to use tables for the package registry. - -/// Represents the package registry at a given account. -pub struct CachedPackageRegistry { - inner: PackageRegistry, -} - -/// Represents the package metadata found in an registry. -pub struct CachedPackageMetadata<'a> { - metadata: &'a PackageMetadata, -} - -/// Represents the package metadata found in an registry. -pub struct CachedModuleMetadata<'a> { - metadata: &'a ModuleMetadata, -} - -impl CachedPackageRegistry { - /// Creates a new registry. - pub async fn create(url: Url, addr: AccountAddress) -> anyhow::Result { - let client = Client::new(url); - // Need to use a different type to deserialize JSON - let inner = client - .get_account_resource_bcs::(addr, "0x1::code::PackageRegistry") - .await? - .into_inner(); - Ok(Self { inner }) - } - - /// Returns the list of packages in this registry by name. - pub fn package_names(&self) -> Vec<&str> { - self.inner - .packages - .iter() - .map(|p| p.name.as_str()) - .collect() - } - - /// Finds the metadata for the given module in the registry by its unique name. - pub async fn get_module<'a>( - &self, - name: impl AsRef, - ) -> anyhow::Result> { - let name = name.as_ref(); - for package in &self.inner.packages { - for module in &package.modules { - if module.name == name { - return Ok(CachedModuleMetadata { metadata: module }); - } - } - } - bail!("module `{}` not found", name) - } - - /// Finds the metadata for the given package in the registry by its unique name. - pub async fn get_package<'a>( - &self, - name: impl AsRef, - ) -> anyhow::Result> { - let name = name.as_ref(); - for package in &self.inner.packages { - if package.name == name { - return Ok(CachedPackageMetadata { metadata: package }); - } - } - bail!("package `{}` not found", name) - } -} - -impl<'a> CachedPackageMetadata<'a> { - pub fn name(&self) -> &str { - &self.metadata.name - } - - pub fn upgrade_policy(&self) -> UpgradePolicy { - self.metadata.upgrade_policy - } - - pub fn upgrade_number(&self) -> u64 { - self.metadata.upgrade_number - } - - pub fn source_digest(&self) -> &str { - &self.metadata.source_digest - } - - pub fn manifest(&self) -> anyhow::Result { - unzip_metadata_str(&self.metadata.manifest) - } - - pub fn module_names(&self) -> Vec<&str> { - self.metadata - .modules - .iter() - .map(|s| s.name.as_str()) - .collect() - } - - pub fn module(&self, name: impl AsRef) -> anyhow::Result> { - let name = name.as_ref(); - for module in &self.metadata.modules { - if module.name == name { - return Ok(CachedModuleMetadata { metadata: module }); - } - } - bail!("module `{}` not found", name) - } - - pub fn save_package_to_disk(&self, path: &Path) -> anyhow::Result<()> { - fs::create_dir_all(path)?; - fs::write( - path.join("Move.toml"), - unzip_metadata_str(&self.metadata.manifest)?, - )?; - let sources_dir = path.join(CompiledPackageLayout::Sources.path()); - fs::create_dir_all(&sources_dir)?; - for module in &self.metadata.modules { - let source = match module.source.is_empty() { - true => { - println!("module without code: {}", module.name); - "".into() - }, - false => unzip_metadata_str(&module.source)?, - }; - fs::write(sources_dir.join(format!("{}.move", module.name)), source)?; - } - Ok(()) - } - - pub fn verify(&self, package_metadata: &PackageMetadata) -> anyhow::Result<()> { - let self_metadata = self.metadata; - - if self_metadata.name != package_metadata.name { - bail!( - "Package name doesn't match {} : {}", - package_metadata.name, - self_metadata.name - ) - } else if self_metadata.deps != package_metadata.deps { - bail!( - "Dependencies don't match {:?} : {:?}", - package_metadata.deps, - self_metadata.deps - ) - } else if self_metadata.modules != package_metadata.modules { - bail!( - "Modules don't match {:?} : {:?}", - package_metadata.modules, - self_metadata.modules - ) - } else if self_metadata.manifest != package_metadata.manifest { - bail!( - "Manifest doesn't match {:?} : {:?}", - package_metadata.manifest, - self_metadata.manifest - ) - } else if self_metadata.upgrade_policy != package_metadata.upgrade_policy { - bail!( - "Upgrade policy doesn't match {:?} : {:?}", - package_metadata.upgrade_policy, - self_metadata.upgrade_policy - ) - } else if self_metadata.upgrade_number != package_metadata.upgrade_number { - bail!( - "Upgrade number doesn't match {:?} : {:?}", - package_metadata.upgrade_number, - self_metadata.upgrade_number - ) - } else if self_metadata.extension != package_metadata.extension { - bail!( - "Extensions doesn't match {:?} : {:?}", - package_metadata.extension, - self_metadata.extension - ) - } else if self_metadata.source_digest != package_metadata.source_digest { - bail!( - "Source digests doesn't match {:?} : {:?}", - package_metadata.source_digest, - self_metadata.source_digest - ) - } - - Ok(()) - } -} - -impl<'a> CachedModuleMetadata<'a> { - pub fn name(&self) -> &str { - &self.metadata.name - } - - pub fn zipped_source(&self) -> &[u8] { - &self.metadata.source - } - - pub fn zipped_source_map_raw(&self) -> &[u8] { - &self.metadata.source_map - } -} diff --git a/m1/m1-cli/src/move_tool/transactional_tests_runner.rs b/m1/m1-cli/src/move_tool/transactional_tests_runner.rs deleted file mode 100644 index 91b504a47..000000000 --- a/m1/m1-cli/src/move_tool/transactional_tests_runner.rs +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::types::{CliError, CliTypedResult}; -/// Most of the code below comes from the crate `datatest-stable`. Because the limitation of `datatest-stable`, -/// we are not able to run transactional tests as a subcommand of the Movement CLI. Therefore, we need to duplicate code -/// here and make minor modifications. -/// -use clap::Parser; -use std::{ - io::{self, Write}, - num::NonZeroUsize, - panic::{catch_unwind, AssertUnwindSafe}, - path::{Path, PathBuf}, - process, - sync::mpsc::{channel, Sender}, - thread, -}; -use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; - -type Result = std::result::Result>; - -/// Run Move transactional tests -#[derive(Parser, Clone)] -pub struct TransactionalTestOpts { - /// The filter string is tested against the name of all tests, and only those tests whose names - /// contain the filter are run. - #[clap(long)] - pub filter: Option, - - /// Exactly match filters rather than match by substring - #[clap(long = "exact")] - pub filter_exact: bool, - - /// Number of threads used for running tests in parallel - #[clap(long, default_value = "32")] - pub test_threads: NonZeroUsize, - - /// Output minimal information - #[clap(long)] - pub quiet: bool, - - /// List all tests - #[clap(long)] - pub list: bool, - - /// Path to contain the tests - #[clap(long, parse(from_os_str))] - pub root_path: PathBuf, - - /// Pattern to match the test files - #[clap(long, default_value = r".*\.(mvir|move)$")] - pub pattern: String, -} - -/// Helper function to iterate through all the files in the given directory, skipping hidden files, -/// and return an iterator of their paths. -pub fn iterate_directory(path: &Path) -> impl Iterator { - walkdir::WalkDir::new(path) - .into_iter() - .map(::std::result::Result::unwrap) - .filter(|entry| { - entry.file_type().is_file() - && entry - .file_name() - .to_str() - .map_or(false, |s| !s.starts_with('.')) // Skip hidden files - }) - .map(|entry| entry.path().to_path_buf()) -} - -pub fn derive_test_name(root: &Path, path: &Path, test_name: &str) -> String { - let relative = path.strip_prefix(root).unwrap_or_else(|_| { - panic!( - "failed to strip prefix '{}' from path '{}'", - root.display(), - path.display() - ) - }); - let mut test_name = test_name.to_string(); - test_name = format!("{}::{}", test_name, relative.display()); - test_name -} - -struct Test { - testfn: Box Result<()> + Send>, - name: String, -} - -enum TestResult { - Ok, - Failed, - FailedWithMsg(String), -} - -pub(crate) fn runner(options: &TransactionalTestOpts, reqs: &[Requirements]) -> CliTypedResult<()> { - let mut tests: Vec = reqs.iter().flat_map(|req| req.expand()).collect(); - tests.sort_by(|a, b| a.name.cmp(&b.name)); - - if options.list { - for test in &tests { - println!("{}: test", test.name); - } - - return Ok(()); - } - - match run_tests(options, tests) { - Ok(true) => Ok(()), - Ok(false) => process::exit(101), - Err(e) => Err(CliError::UnexpectedError(format!( - "error: io error when running tests: {:?}", - e - ))), - } -} - -fn run_tests(options: &TransactionalTestOpts, tests: Vec) -> io::Result { - let total = tests.len(); - - // Filter out tests - let mut remaining = match &options.filter { - None => tests, - Some(filter) => tests - .into_iter() - .filter(|test| { - if options.filter_exact { - test.name == filter[..] - } else { - test.name.contains(&filter[..]) - } - }) - .rev() - .collect(), - }; - - let filtered_out = total - remaining.len(); - let mut summary = TestSummary::new(total, filtered_out); - - if !options.quiet { - summary.write_starting_msg()?; - } - - let (tx, rx) = channel(); - - let mut pending = 0; - while pending > 0 || !remaining.is_empty() { - while pending < options.test_threads.get() && !remaining.is_empty() { - let test = remaining.pop().unwrap(); - run_test(test, tx.clone()); - pending += 1; - } - - let (name, result) = rx.recv().unwrap(); - summary.handle_result(name, result)?; - - pending -= 1; - } - - // Write Test Summary - if !options.quiet { - summary.write_summary()?; - } - - Ok(summary.success()) -} - -fn run_test(test: Test, channel: Sender<(String, TestResult)>) { - let Test { name, testfn } = test; - - let cfg = thread::Builder::new().name(name.clone()); - cfg.spawn(move || { - let result = match catch_unwind(AssertUnwindSafe(testfn)) { - Ok(Ok(())) => TestResult::Ok, - Ok(Err(e)) => TestResult::FailedWithMsg(format!("{:?}", e)), - Err(_) => TestResult::Failed, - }; - - channel.send((name, result)).unwrap(); - }) - .unwrap(); -} - -struct TestSummary { - stdout: StandardStream, - total: usize, - filtered_out: usize, - passed: usize, - failed: Vec, -} - -impl TestSummary { - fn new(total: usize, filtered_out: usize) -> Self { - Self { - stdout: StandardStream::stdout(ColorChoice::Auto), - total, - filtered_out, - passed: 0, - failed: Vec::new(), - } - } - - fn handle_result(&mut self, name: String, result: TestResult) -> io::Result<()> { - write!(self.stdout, "test {} ... ", name)?; - match result { - TestResult::Ok => { - self.passed += 1; - self.write_ok()?; - }, - TestResult::Failed => { - self.failed.push(name); - self.write_failed()?; - }, - TestResult::FailedWithMsg(msg) => { - self.failed.push(name); - self.write_failed()?; - writeln!(self.stdout)?; - - write!(self.stdout, "Error: {}", msg)?; - }, - } - writeln!(self.stdout)?; - Ok(()) - } - - fn write_ok(&mut self) -> io::Result<()> { - self.stdout - .set_color(ColorSpec::new().set_fg(Some(Color::Green)))?; - write!(self.stdout, "ok")?; - self.stdout.reset()?; - Ok(()) - } - - fn write_failed(&mut self) -> io::Result<()> { - self.stdout - .set_color(ColorSpec::new().set_fg(Some(Color::Red)))?; - write!(self.stdout, "FAILED")?; - self.stdout.reset()?; - Ok(()) - } - - fn write_starting_msg(&mut self) -> io::Result<()> { - writeln!(self.stdout)?; - writeln!( - self.stdout, - "running {} tests", - self.total - self.filtered_out - )?; - Ok(()) - } - - fn write_summary(&mut self) -> io::Result<()> { - // Print out the failing tests - if !self.failed.is_empty() { - writeln!(self.stdout)?; - writeln!(self.stdout, "failures:")?; - for name in &self.failed { - writeln!(self.stdout, " {}", name)?; - } - } - - writeln!(self.stdout)?; - write!(self.stdout, "test result: ")?; - if self.failed.is_empty() { - self.write_ok()?; - } else { - self.write_failed()?; - } - writeln!( - self.stdout, - ". {} passed; {} failed; {} filtered out", - self.passed, - self.failed.len(), - self.filtered_out - )?; - writeln!(self.stdout)?; - Ok(()) - } - - fn success(&self) -> bool { - self.failed.is_empty() - } -} - -#[doc(hidden)] -pub struct Requirements { - test: fn(&Path) -> Result<()>, - test_name: String, - root: String, - pattern: String, -} - -impl Requirements { - #[doc(hidden)] - pub fn new( - test: fn(&Path) -> Result<()>, - test_name: String, - root: String, - pattern: String, - ) -> Self { - Self { - test, - test_name, - root, - pattern, - } - } - - /// Generate standard test descriptors ([`test::TestDescAndFn`]) from the descriptor of - /// `#[datatest::files(..)]`. - /// - /// Scans all files in a given directory, finds matching ones and generates a test descriptor - /// for each of them. - fn expand(&self) -> Vec { - let root = Path::new(&self.root).to_path_buf(); - - let re = regex::Regex::new(&self.pattern) - .unwrap_or_else(|_| panic!("invalid regular expression: '{}'", self.pattern)); - - let tests: Vec<_> = iterate_directory(&root) - .filter_map(|path| { - let input_path = path.to_string_lossy(); - if re.is_match(&input_path) { - let testfn = self.test; - let name = derive_test_name(&root, &path, &self.test_name); - let testfn = Box::new(move || (testfn)(&path)); - - Some(Test { testfn, name }) - } else { - None - } - }) - .collect(); - - // We want to avoid silent fails due to typos in regexp! - if tests.is_empty() { - panic!( - "no test cases found for test '{}'. Scanned directory: '{}' with pattern '{}'", - self.test_name, self.root, self.pattern, - ); - } - - tests - } -} diff --git a/m1/m1-cli/src/node/analyze/analyze_validators.rs b/m1/m1-cli/src/node/analyze/analyze_validators.rs deleted file mode 100644 index a5fa20b10..000000000 --- a/m1/m1-cli/src/node/analyze/analyze_validators.rs +++ /dev/null @@ -1,540 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::fetch_metadata::ValidatorInfo; -use anyhow::Result; -use aptos_bitvec::BitVec; -use aptos_rest_client::VersionedNewBlockEvent; -use aptos_storage_interface::{DbReader, Order}; -use aptos_types::{ - account_address::AccountAddress, - account_config::{new_block_event_key, NewBlockEvent}, -}; -use itertools::Itertools; -use std::{cmp::Ordering, collections::HashMap, convert::TryFrom, ops::Add}; - -/// Single validator stats -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct ValidatorStats { - /// Number of successful proposals - pub proposal_successes: u32, - /// Number of failed proposals - pub proposal_failures: u32, - /// Number of votes proposals - pub votes: u32, - /// Number of transactions in a block - pub transactions: u32, - /// Voting power - pub voting_power: u64, -} - -impl ValidatorStats { - /// Proposal failure rate - pub fn failure_rate(&self) -> f32 { - (self.proposal_failures as f32) / (self.proposal_failures + self.proposal_successes) as f32 - } - - /// Whether node is proposing well enough - pub fn is_reliable(&self) -> bool { - (self.proposal_successes > 0) && (self.failure_rate() < 0.1) - } - - // Whether node is voting well enough - pub fn is_voting_enough(&self, rounds: u32) -> bool { - self.votes as f32 > rounds as f32 * 0.3 - } -} - -#[derive(Debug, Eq, PartialEq, Hash)] -pub enum NodeState { - // Proposal failure < 10%, >30% votes - Reliable, - // Proposal failure < 10%, <30% votes - ReliableLowVotes, - // Has successful proposals, but proposal failure > 10% - AliveUnreliable, - // No successful proposals, but voting - OnlyVoting, - // Not participating in consensus - NotParticipatingInConsensus, - // Not in ValidatorSet - Absent, -} - -impl NodeState { - pub fn to_char(&self) -> &str { - match self { - Self::Reliable => "+", - Self::ReliableLowVotes => "P", - Self::AliveUnreliable => "~", - Self::OnlyVoting => "V", - Self::NotParticipatingInConsensus => "X", - Self::Absent => " ", - } - } - - // Large the value, the worse the node is performing. - pub fn to_order_weight(&self) -> usize { - match self { - Self::Reliable => 0, - Self::ReliableLowVotes => 100, - Self::AliveUnreliable => 10000, - Self::OnlyVoting => 1000000, - Self::NotParticipatingInConsensus => 100000000, - Self::Absent => 1, - } - } -} - -impl Add for ValidatorStats { - type Output = Self; - - fn add(self, other: Self) -> Self { - Self { - proposal_successes: self.proposal_successes + other.proposal_successes, - proposal_failures: self.proposal_failures + other.proposal_failures, - votes: self.votes + other.votes, - transactions: self.transactions + other.transactions, - voting_power: 0, // cannot aggregate voting power. - } - } -} - -/// Statistics for all validators -#[derive(Clone)] -pub struct EpochStats { - /// Statistics for each of the validators - pub validator_stats: HashMap, - /// Total rounds in an epoch - pub total_rounds: u32, - /// Total transactions in an epoch - pub total_transactions: u32, - /// Successful rounds in an epoch - pub round_successes: u32, - /// Failed rounds in an epoch - pub round_failures: u32, - /// Nil blocks in an epoch - pub nil_blocks: u32, - /// Total voting power - pub total_voting_power: u128, -} - -impl EpochStats { - pub fn to_state(&self, validator: &AccountAddress) -> NodeState { - self.validator_stats - .get(validator) - .map(|b| { - if b.is_reliable() { - if b.is_voting_enough(self.total_rounds) { - NodeState::Reliable - } else { - NodeState::ReliableLowVotes - } - } else if b.proposal_successes > 0 { - NodeState::AliveUnreliable - } else if b.votes > 0 { - NodeState::OnlyVoting - } else { - NodeState::NotParticipatingInConsensus - } - }) - .unwrap_or(NodeState::Absent) - } - - pub fn to_votes(&self, validator: &AccountAddress) -> u32 { - self.validator_stats - .get(validator) - .map(|s| s.votes) - .unwrap_or(0) - } - - pub fn to_voting_power(&self, validator: &AccountAddress) -> u64 { - self.validator_stats - .get(validator) - .map(|s| s.voting_power) - .unwrap_or(0) - } -} - -impl Add for EpochStats { - type Output = Self; - - fn add(self, other: Self) -> Self { - let mut validator_stats = self.validator_stats; - for (key, other_validator_stats) in other.validator_stats.into_iter() { - validator_stats.insert( - key, - other_validator_stats - + *validator_stats.get(&key).unwrap_or(&ValidatorStats { - proposal_failures: 0, - proposal_successes: 0, - votes: 0, - transactions: 0, - voting_power: 0, - }), - ); - } - Self { - validator_stats, - total_rounds: self.total_rounds + other.total_rounds, - round_successes: self.round_successes + other.round_successes, - round_failures: self.round_failures + other.round_failures, - nil_blocks: self.nil_blocks + other.nil_blocks, - total_transactions: self.total_transactions + other.total_transactions, - total_voting_power: 0, - } - } -} - -/// Analyze validator performance -pub struct AnalyzeValidators {} - -impl AnalyzeValidators { - /// Fetch all events from a single epoch from DB. - pub fn fetch_epoch(epoch: u64, aptos_db: &dyn DbReader) -> Result> { - let batch = 100; - - let mut cursor = u64::max_value(); - let mut result: Vec = vec![]; - let ledger_version = aptos_db.get_latest_ledger_info()?.ledger_info().version(); - - loop { - let raw_events = aptos_db.get_events( - &new_block_event_key(), - cursor, - Order::Descending, - batch as u64, - ledger_version, - )?; - let end = raw_events.len() < batch; - for raw_event in raw_events { - if cursor <= raw_event.event.sequence_number() { - println!( - "Duplicate event found for {} : {:?}", - cursor, - raw_event.event.sequence_number() - ); - } else { - cursor = raw_event.event.sequence_number(); - let event = bcs::from_bytes::(raw_event.event.event_data())?; - - match epoch.cmp(&event.epoch()) { - Ordering::Equal => { - result.push(VersionedNewBlockEvent { - event, - version: raw_event.transaction_version, - sequence_number: raw_event.event.sequence_number(), - }); - }, - Ordering::Greater => { - return Ok(result); - }, - Ordering::Less => {}, - }; - } - } - - if end { - return Ok(result); - } - } - } - - /// Analyze single epoch - pub fn analyze(blocks: &[VersionedNewBlockEvent], validators: &[ValidatorInfo]) -> EpochStats { - assert!( - validators.iter().as_slice().windows(2).all(|w| { - w[0].validator_index - .partial_cmp(&w[1].validator_index) - .map(|o| o != Ordering::Greater) - .unwrap_or(false) - }), - "Validators need to be sorted" - ); - assert!( - blocks.iter().as_slice().windows(2).all(|w| { - w[0].event - .round() - .partial_cmp(&w[1].event.round()) - .map(|o| o != Ordering::Greater) - .unwrap_or(false) - }), - "Blocks need to be sorted" - ); - - let mut successes = HashMap::::new(); - let mut failures = HashMap::::new(); - let mut votes = HashMap::::new(); - let mut transactions = HashMap::::new(); - - let mut trimmed_rounds = 0; - let mut nil_blocks = 0; - let mut previous_round = 0; - for (pos, block) in blocks.iter().enumerate() { - let event = &block.event; - let is_nil = event.proposer() == AccountAddress::ZERO; - if is_nil { - nil_blocks += 1; - } - let expected_round = - previous_round + u64::from(!is_nil) + event.failed_proposer_indices().len() as u64; - if event.round() != expected_round { - println!( - "Missing failed AccountAddresss : {} {:?}", - previous_round, &event - ); - assert!(expected_round < event.round()); - trimmed_rounds += event.round() - expected_round; - } - previous_round = event.round(); - - if !is_nil { - *successes.entry(event.proposer()).or_insert(0) += 1; - } - - for failed_proposer_index in event.failed_proposer_indices() { - *failures - .entry(validators[*failed_proposer_index as usize].address) - .or_insert(0) += 1; - } - - let previous_block_votes_bitvec: BitVec = - event.previous_block_votes_bitvec().clone().into(); - assert_eq!( - BitVec::required_buckets(validators.len() as u16), - previous_block_votes_bitvec.num_buckets() - ); - for (i, validator) in validators.iter().enumerate() { - if previous_block_votes_bitvec.is_set(i as u16) { - *votes.entry(validator.address).or_insert(0) += 1; - } - } - - let cur_transactions_option = blocks - .get(pos + 1) - .map(|next| u32::try_from(next.version - block.version - 2).unwrap()); - if let Some(cur_transactions) = cur_transactions_option { - if is_nil { - assert_eq!( - cur_transactions, - 0, - "{} {:?}", - block.version, - blocks.get(pos + 1) - ); - } - *transactions.entry(event.proposer()).or_insert(0) += cur_transactions; - } - } - let total_successes: u32 = successes.values().sum(); - let total_failures: u32 = failures.values().sum(); - let total_transactions: u32 = transactions.values().sum(); - let total_rounds = total_successes + total_failures; - assert_eq!( - total_rounds + u32::try_from(trimmed_rounds).unwrap(), - previous_round as u32, - "{} success + {} failures + {} trimmed != {}", - total_successes, - total_failures, - trimmed_rounds, - previous_round - ); - - return EpochStats { - validator_stats: validators - .iter() - .map(|validator| { - (validator.address, ValidatorStats { - proposal_successes: *successes.get(&validator.address).unwrap_or(&0), - proposal_failures: *failures.get(&validator.address).unwrap_or(&0), - votes: *votes.get(&validator.address).unwrap_or(&0), - transactions: *transactions.get(&validator.address).unwrap_or(&0), - voting_power: validator.voting_power, - }) - }) - .collect(), - total_rounds, - total_transactions, - round_successes: total_successes, - round_failures: total_failures, - nil_blocks, - total_voting_power: validators - .iter() - .map(|validator| validator.voting_power as u128) - .sum(), - }; - } - - /// Print validator stats in a table - pub fn print_detailed_epoch_table( - epoch_stats: &EpochStats, - extra: Option<(&str, &HashMap)>, - sort_by_health: bool, - ) { - println!( - "Rounds: {} successes, {} failures, {} NIL blocks, failure rate: {}%, nil block rate: {}%", - epoch_stats.round_successes, epoch_stats.round_failures, epoch_stats.nil_blocks, - 100.0 * epoch_stats.round_failures as f32 / epoch_stats.total_rounds as f32, - 100.0 * epoch_stats.nil_blocks as f32 / epoch_stats.total_rounds as f32, - ); - println!( - "{: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <30}", - "elected", - "% rounds", - "% failed", - "succeded", - "failed", - "voted", - "transact", - extra.map(|(column, _)| column).unwrap_or("") - ); - - let mut validator_order: Vec<&AccountAddress> = - epoch_stats.validator_stats.keys().collect(); - if sort_by_health { - validator_order.sort_by_cached_key(|v| { - epoch_stats - .validator_stats - .get(v) - .map(|s| { - ( - if s.proposal_successes > 0 { - (s.failure_rate() * 100000.0) as u32 - } else { - 200000 - }, - -((s.proposal_failures + s.proposal_successes) as i32), - *v, - ) - }) - .unwrap() - }); - } else { - validator_order.sort(); - } - - for validator in validator_order { - let cur_stats = epoch_stats.validator_stats.get(validator).unwrap(); - println!( - "{: <10} | {:5.2}% | {:7.3}% | {: <10} | {: <10} | {: <10} | {: <10} | {}", - cur_stats.proposal_failures + cur_stats.proposal_successes, - 100.0 * (cur_stats.proposal_failures + cur_stats.proposal_successes) as f32 - / (epoch_stats.total_rounds as f32), - 100.0 * cur_stats.failure_rate(), - cur_stats.proposal_successes, - cur_stats.proposal_failures, - cur_stats.votes, - cur_stats.transactions, - if let Some((_, extra_map)) = extra { - format!( - "{: <30} | {}", - extra_map.get(validator).unwrap_or(&"".to_string()), - validator - ) - } else { - format!("{}", validator) - } - ); - } - } - - pub fn print_validator_health_over_time( - stats: &HashMap, - validators: &[AccountAddress], - extra: Option<&HashMap>, - ) { - let epochs: Vec<_> = stats.keys().sorted().collect(); - - let mut sorted_validators = validators.to_vec(); - sorted_validators.sort_by_cached_key(|validator| { - ( - epochs - .iter() - .map(|cur_epoch| { - stats - .get(cur_epoch) - .unwrap() - .to_state(validator) - .to_order_weight() - }) - .sum::(), - *validator, - ) - }); - - for validator in sorted_validators { - print!( - "{}: ", - if let Some(extra_map) = extra { - format!( - "{: <30} | {}", - extra_map.get(&validator).unwrap_or(&""), - validator - ) - } else { - format!("{}", validator) - } - ); - for cur_epoch in epochs.iter() { - print!( - "{}", - stats.get(cur_epoch).unwrap().to_state(&validator).to_char() - ); - } - println!(); - } - } - - pub fn print_network_health_over_time( - stats: &HashMap, - validators: &[AccountAddress], - ) { - let epochs = stats.keys().sorted(); - - println!( - "{: <8} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10}", - "epoch", - "reliable", - "r low vote", - "unreliable", - "only vote", - "down(cons)", - "rounds", - "#r failed", - "% failure", - "% stake has >10% of votes", - ); - for cur_epoch in epochs { - let epoch_stats = stats.get(cur_epoch).unwrap(); - - let counts = validators.iter().map(|v| epoch_stats.to_state(v)).counts(); - - let voted_voting_power: u128 = validators - .iter() - .flat_map(|v| { - if epoch_stats.to_votes(v) > epoch_stats.round_successes / 10 { - Some(epoch_stats.to_voting_power(v) as u128) - } else { - None - } - }) - .sum(); - - println!( - "{: <8} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {:10.2} | {:10.2}", - cur_epoch, - counts.get(&NodeState::Reliable).unwrap_or(&0), - counts.get(&NodeState::ReliableLowVotes).unwrap_or(&0), - counts.get(&NodeState::AliveUnreliable).unwrap_or(&0), - counts.get(&NodeState::OnlyVoting).unwrap_or(&0), - counts - .get(&NodeState::NotParticipatingInConsensus) - .unwrap_or(&0), - epoch_stats.total_rounds, - epoch_stats.round_failures, - 100.0 * epoch_stats.round_failures as f32 / epoch_stats.total_rounds as f32, - 100.0 * voted_voting_power as f32 / epoch_stats.total_voting_power as f32, - ); - } - } -} diff --git a/m1/m1-cli/src/node/analyze/fetch_metadata.rs b/m1/m1-cli/src/node/analyze/fetch_metadata.rs deleted file mode 100644 index e9ab24e8b..000000000 --- a/m1/m1-cli/src/node/analyze/fetch_metadata.rs +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::{anyhow, Result}; -use aptos_rest_client::{ - aptos_api_types::{IdentifierWrapper, MoveResource, WriteSetChange}, - Client as RestClient, Transaction, VersionedNewBlockEvent, -}; -use aptos_types::account_address::AccountAddress; -use std::str::FromStr; - -const MAX_FETCH_BATCH_SIZE: u16 = 1000; - -#[derive(Eq, PartialEq, Clone, Copy, Debug)] -pub struct ValidatorInfo { - pub address: AccountAddress, - pub voting_power: u64, - pub validator_index: u16, -} - -pub struct EpochInfo { - pub epoch: u64, - pub blocks: Vec, - pub validators: Vec, - pub partial: bool, -} - -pub struct FetchMetadata {} - -impl FetchMetadata { - fn get_validator_addresses( - data: &MoveResource, - field_name: &str, - ) -> Result> { - fn extract_validator_address(validator: &serde_json::Value) -> Result { - Ok(ValidatorInfo { - address: AccountAddress::from_hex_literal( - validator.get("addr").unwrap().as_str().unwrap(), - ) - .map_err(|e| anyhow!("Cannot parse address {:?}", e))?, - voting_power: validator - .get("voting_power") - .unwrap() - .as_str() - .unwrap() - .parse() - .map_err(|e| anyhow!("Cannot parse voting_power {:?}", e))?, - validator_index: validator - .get("config") - .unwrap() - .get("validator_index") - .unwrap() - .as_str() - .unwrap() - .parse() - .map_err(|e| anyhow!("Cannot parse validator_index {:?}", e))?, - }) - } - - let validators_json = data - .data - .0 - .get(&IdentifierWrapper::from_str(field_name).unwrap()) - .unwrap(); - if let serde_json::Value::Array(validators_array) = validators_json { - let mut validators: Vec = vec![]; - for validator in validators_array { - validators.push(extract_validator_address(validator)?); - } - Ok(validators) - } else { - Err(anyhow!("{} validators not in json", field_name)) - } - } - - async fn get_transactions_in_range( - client: &RestClient, - start: u64, - last: u64, - ) -> Result> { - let mut result = Vec::new(); - let mut cursor = start; - while cursor < last { - let limit = std::cmp::min(MAX_FETCH_BATCH_SIZE as u64, last - cursor) as u16; - let mut current = client - .get_transactions(Some(cursor), Some(limit)) - .await? - .into_inner(); - if current.is_empty() { - return Err(anyhow!( - "No transactions returned with start={} and limit={}", - cursor, - limit - )); - } - cursor += current.len() as u64; - result.append(&mut current); - } - Ok(result) - } - - fn get_validators_from_transaction(transaction: &Transaction) -> Result> { - if let Ok(info) = transaction.transaction_info() { - for change in &info.changes { - if let WriteSetChange::WriteResource(resource) = change { - if resource.data.typ.name.0.as_str() == "ValidatorSet" { - // No pending at epoch change - assert_eq!( - Vec::::new(), - FetchMetadata::get_validator_addresses( - &resource.data, - "pending_inactive" - )? - ); - assert_eq!( - Vec::::new(), - FetchMetadata::get_validator_addresses( - &resource.data, - "pending_active" - )? - ); - return FetchMetadata::get_validator_addresses( - &resource.data, - "active_validators", - ); - } - } - } - } - Err(anyhow!("Couldn't find ValidatorSet in the transaction")) - } - - pub async fn fetch_new_block_events( - client: &RestClient, - start_epoch: Option, - end_epoch: Option, - ) -> Result> { - let (last_events, state) = client - .get_new_block_events_bcs(None, Some(1)) - .await? - .into_parts(); - let mut start_seq_num = state.oldest_block_height; - assert_eq!(last_events.len(), 1, "{:?}", last_events); - let last_event = last_events.first().unwrap(); - let last_seq_num = last_event.sequence_number; - - let wanted_start_epoch = { - let mut wanted_start_epoch = start_epoch.unwrap_or(2); - if wanted_start_epoch < 0 { - wanted_start_epoch = last_event.event.epoch() as i64 + wanted_start_epoch + 1; - } - - let oldest_event = client - .get_new_block_events_bcs(Some(start_seq_num), Some(1)) - .await? - .into_inner() - .into_iter() - .next() - .ok_or_else(|| anyhow!("No blocks at oldest_block_height {}", start_seq_num))?; - let oldest_fetchable_epoch = std::cmp::max(oldest_event.event.epoch() + 1, 2); - if oldest_fetchable_epoch > wanted_start_epoch as u64 { - println!( - "Oldest full epoch that can be retreived is {} ", - oldest_fetchable_epoch - ); - oldest_fetchable_epoch - } else { - wanted_start_epoch as u64 - } - }; - let wanted_end_epoch = { - let mut wanted_end_epoch = end_epoch.unwrap_or(i64::MAX); - if wanted_end_epoch < 0 { - wanted_end_epoch = last_event.event.epoch() as i64 + wanted_end_epoch + 1; - } - std::cmp::min( - last_event.event.epoch() + 1, - std::cmp::max(2, wanted_end_epoch) as u64, - ) - }; - - if wanted_start_epoch > 2 { - let mut search_end = last_seq_num; - - // Stop when search is close enough, and we can then linearly - // proceed from there. - // Since we are ignoring results we are fetching during binary search - // we want to stop when we are close. - while start_seq_num + 20 < search_end { - let mid = (start_seq_num + search_end) / 2; - - let mid_epoch = client - .get_new_block_events_bcs(Some(mid), Some(1)) - .await? - .into_inner() - .first() - .unwrap() - .event - .epoch(); - - if mid_epoch < wanted_start_epoch { - start_seq_num = mid; - } else { - search_end = mid; - } - } - } - - let mut batch_index = 0; - - println!( - "Fetching {} to {} sequence number, wanting epochs [{}, {}), last version: {} and epoch: {}", - start_seq_num, last_seq_num, wanted_start_epoch, wanted_end_epoch, state.version, state.epoch, - ); - let mut result: Vec = vec![]; - if wanted_start_epoch >= wanted_end_epoch { - return Ok(result); - } - - let mut validators: Vec = vec![]; - let mut current: Vec = vec![]; - let mut epoch = 0; - - let mut cursor = start_seq_num; - loop { - let response = client - .get_new_block_events_bcs(Some(cursor), Some(MAX_FETCH_BATCH_SIZE)) - .await; - - if response.is_err() { - println!( - "Failed to read new_block_events beyond {}, stopping. {:?}", - cursor, - response.unwrap_err() - ); - assert!(!validators.is_empty()); - result.push(EpochInfo { - epoch, - blocks: current, - validators: validators.clone(), - partial: true, - }); - return Ok(result); - } - let events = response.unwrap().into_inner(); - - if events.is_empty() { - return Err(anyhow!( - "No transactions returned with start={} and limit={}", - cursor, - MAX_FETCH_BATCH_SIZE - )); - } - - cursor += events.len() as u64; - batch_index += 1; - - for event in events { - if event.event.epoch() > epoch { - if epoch == 0 { - epoch = event.event.epoch(); - current = vec![]; - } else { - let last = current.last().cloned(); - if let Some(last) = last { - let transactions = FetchMetadata::get_transactions_in_range( - client, - last.version, - event.version, - ) - .await?; - assert_eq!( - transactions.first().unwrap().version().unwrap(), - last.version - ); - for transaction in transactions { - if let Ok(new_validators) = - FetchMetadata::get_validators_from_transaction(&transaction) - { - if epoch >= wanted_start_epoch { - assert!(!validators.is_empty()); - result.push(EpochInfo { - epoch, - blocks: current, - validators: validators.clone(), - partial: false, - }); - } - current = vec![]; - - validators = new_validators; - validators.sort_by_key(|v| v.validator_index); - assert_eq!(epoch + 1, event.event.epoch()); - epoch = event.event.epoch(); - if epoch >= wanted_end_epoch { - return Ok(result); - } - break; - } - } - assert!( - current.is_empty(), - "Couldn't find ValidatorSet change for transactions start={}, limit={} for epoch {}", - last.version, - event.version - last.version, - event.event.epoch(), - ); - } - } - } - current.push(event); - } - - if batch_index % 100 == 0 { - println!( - "Fetched {} epochs (in epoch {} with {} blocks) from {} NewBlockEvents", - result.len(), - epoch, - current.len(), - cursor - ); - } - - if cursor > last_seq_num { - if !validators.is_empty() { - result.push(EpochInfo { - epoch, - blocks: current, - validators: validators.clone(), - partial: true, - }); - } - return Ok(result); - } - } - } -} diff --git a/m1/m1-cli/src/node/analyze/mod.rs b/m1/m1-cli/src/node/analyze/mod.rs deleted file mode 100644 index ac8c70163..000000000 --- a/m1/m1-cli/src/node/analyze/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod analyze_validators; -pub mod fetch_metadata; diff --git a/m1/m1-cli/src/node/mod.rs b/m1/m1-cli/src/node/mod.rs deleted file mode 100644 index 33caaac4e..000000000 --- a/m1/m1-cli/src/node/mod.rs +++ /dev/null @@ -1,1730 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod analyze; - -use crate::{ - common::{ - types::{ - CliCommand, CliError, CliResult, CliTypedResult, ConfigSearchMode, - OptionalPoolAddressArgs, PoolAddressArgs, ProfileOptions, PromptOptions, RestOptions, - TransactionOptions, TransactionSummary, - }, - utils::{prompt_yes_with_override, read_from_file}, - }, - config::GlobalConfig, - genesis::git::from_yaml, - node::analyze::{ - analyze_validators::{AnalyzeValidators, ValidatorStats}, - fetch_metadata::FetchMetadata, - }, -}; -use aptos_backup_cli::{ - coordinators::restore::{RestoreCoordinator, RestoreCoordinatorOpt}, - metadata::cache::MetadataCacheOpt, - storage::command_adapter::{config::CommandAdapterConfig, CommandAdapter}, - utils::{ConcurrentDownloadsOpt, GlobalRestoreOpt, ReplayConcurrencyLevelOpt, RocksdbOpt}, -}; -use aptos_cached_packages::aptos_stdlib; -use aptos_config::config::NodeConfig; -use aptos_crypto::{bls12381, bls12381::PublicKey, x25519, ValidCryptoMaterialStringExt}; -use aptos_faucet_core::server::{FunderKeyEnum, RunConfig}; -use aptos_genesis::config::{HostAndPort, OperatorConfiguration}; -use aptos_network_checker::args::{ - validate_address, CheckEndpointArgs, HandshakeArgs, NodeAddressArgs, -}; -use aptos_rest_client::{aptos_api_types::VersionedEvent, Client, State}; -use aptos_types::{ - account_address::AccountAddress, - account_config::{BlockResource, CORE_CODE_ADDRESS}, - chain_id::ChainId, - network_address::NetworkAddress, - on_chain_config::{ConfigurationResource, ConsensusScheme, ValidatorSet}, - stake_pool::StakePool, - staking_contract::StakingContractStore, - validator_info::ValidatorInfo, - validator_performances::ValidatorPerformances, - vesting::VestingAdminStore, -}; -use async_trait::async_trait; -use bcs::Result; -use chrono::{DateTime, NaiveDateTime, Utc}; -use clap::Parser; -use futures::FutureExt; -use hex::FromHex; -use rand::{rngs::StdRng, SeedableRng}; -use reqwest::Url; -use serde::{Deserialize, Serialize}; -use std::{ - collections::HashMap, - convert::{TryFrom, TryInto}, - path::PathBuf, - pin::Pin, - sync::Arc, - thread, - time::Duration, -}; -use tokio::time::Instant; - -const SECS_TO_MICROSECS: u64 = 1_000_000; - -/// Tool for operations related to nodes -/// -/// This tool allows you to run a local test node for testing, -/// identify issues with nodes, and show related information. -#[derive(Parser)] -pub enum NodeTool { - AnalyzeValidatorPerformance(AnalyzeValidatorPerformance), - BootstrapDbFromBackup(BootstrapDbFromBackup), - CheckNetworkConnectivity(CheckNetworkConnectivity), - GetPerformance(GetPerformance), - GetStakePool(GetStakePool), - InitializeValidator(InitializeValidator), - JoinValidatorSet(JoinValidatorSet), - LeaveValidatorSet(LeaveValidatorSet), - ShowEpochInfo(ShowEpochInfo), - ShowValidatorConfig(ShowValidatorConfig), - ShowValidatorSet(ShowValidatorSet), - ShowValidatorStake(ShowValidatorStake), - RunLocalTestnet(RunLocalTestnet), - UpdateConsensusKey(UpdateConsensusKey), - UpdateValidatorNetworkAddresses(UpdateValidatorNetworkAddresses), -} - -impl NodeTool { - pub async fn execute(self) -> CliResult { - use NodeTool::*; - match self { - AnalyzeValidatorPerformance(tool) => tool.execute_serialized().await, - BootstrapDbFromBackup(tool) => tool.execute_serialized().await, - CheckNetworkConnectivity(tool) => tool.execute_serialized().await, - GetPerformance(tool) => tool.execute_serialized().await, - GetStakePool(tool) => tool.execute_serialized().await, - InitializeValidator(tool) => tool.execute_serialized().await, - JoinValidatorSet(tool) => tool.execute_serialized().await, - LeaveValidatorSet(tool) => tool.execute_serialized().await, - ShowEpochInfo(tool) => tool.execute_serialized().await, - ShowValidatorSet(tool) => tool.execute_serialized().await, - ShowValidatorStake(tool) => tool.execute_serialized().await, - ShowValidatorConfig(tool) => tool.execute_serialized().await, - RunLocalTestnet(tool) => tool.execute_serialized_without_logger().await, - UpdateConsensusKey(tool) => tool.execute_serialized().await, - UpdateValidatorNetworkAddresses(tool) => tool.execute_serialized().await, - } - } -} - -#[derive(Parser)] -pub struct OperatorConfigFileArgs { - /// Operator Configuration file - /// - /// Config file created from the `genesis set-validator-configuration` command - #[clap(long, parse(from_os_str))] - pub(crate) operator_config_file: Option, -} - -impl OperatorConfigFileArgs { - fn load(&self) -> CliTypedResult> { - if let Some(ref file) = self.operator_config_file { - Ok(from_yaml( - &String::from_utf8(read_from_file(file)?).map_err(CliError::from)?, - )?) - } else { - Ok(None) - } - } -} - -#[derive(Parser)] -pub struct ValidatorConsensusKeyArgs { - /// Hex encoded Consensus public key - /// - /// The key should be a BLS12-381 public key - #[clap(long, parse(try_from_str = bls12381::PublicKey::from_encoded_string))] - pub(crate) consensus_public_key: Option, - - /// Hex encoded Consensus proof of possession - /// - /// The key should be a BLS12-381 proof of possession - #[clap(long, parse(try_from_str = bls12381::ProofOfPossession::from_encoded_string))] - pub(crate) proof_of_possession: Option, -} - -impl ValidatorConsensusKeyArgs { - fn get_consensus_public_key<'a>( - &'a self, - operator_config: &'a Option, - ) -> CliTypedResult<&'a bls12381::PublicKey> { - let consensus_public_key = if let Some(ref consensus_public_key) = self.consensus_public_key - { - consensus_public_key - } else if let Some(ref operator_config) = operator_config { - &operator_config.consensus_public_key - } else { - return Err(CliError::CommandArgumentError( - "Must provide either --operator-config-file or --consensus-public-key".to_string(), - )); - }; - Ok(consensus_public_key) - } - - fn get_consensus_proof_of_possession<'a>( - &'a self, - operator_config: &'a Option, - ) -> CliTypedResult<&'a bls12381::ProofOfPossession> { - let proof_of_possession = if let Some(ref proof_of_possession) = self.proof_of_possession { - proof_of_possession - } else if let Some(ref operator_config) = operator_config { - &operator_config.consensus_proof_of_possession - } else { - return Err(CliError::CommandArgumentError( - "Must provide either --operator-config-file or --proof-of-possession".to_string(), - )); - }; - Ok(proof_of_possession) - } -} - -#[derive(Parser)] -pub struct ValidatorNetworkAddressesArgs { - /// Host and port pair for the validator - /// - /// e.g. 127.0.0.1:6180 - #[clap(long)] - pub(crate) validator_host: Option, - - /// Validator x25519 public network key - #[clap(long, parse(try_from_str = x25519::PublicKey::from_encoded_string))] - pub(crate) validator_network_public_key: Option, - - /// Host and port pair for the fullnode - /// - /// e.g. 127.0.0.1:6180. Optional - #[clap(long)] - pub(crate) full_node_host: Option, - - /// Full node x25519 public network key - #[clap(long, parse(try_from_str = x25519::PublicKey::from_encoded_string))] - pub(crate) full_node_network_public_key: Option, -} - -impl ValidatorNetworkAddressesArgs { - fn get_network_configs<'a>( - &'a self, - operator_config: &'a Option, - ) -> CliTypedResult<( - x25519::PublicKey, - Option, - &'a HostAndPort, - Option<&'a HostAndPort>, - )> { - let validator_network_public_key = - if let Some(public_key) = self.validator_network_public_key { - public_key - } else if let Some(ref operator_config) = operator_config { - operator_config.validator_network_public_key - } else { - return Err(CliError::CommandArgumentError( - "Must provide either --operator-config-file or --validator-network-public-key" - .to_string(), - )); - }; - - let full_node_network_public_key = - if let Some(public_key) = self.full_node_network_public_key { - Some(public_key) - } else if let Some(ref operator_config) = operator_config { - operator_config.full_node_network_public_key - } else { - None - }; - - let validator_host = if let Some(ref host) = self.validator_host { - host - } else if let Some(ref operator_config) = operator_config { - &operator_config.validator_host - } else { - return Err(CliError::CommandArgumentError( - "Must provide either --operator-config-file or --validator-host".to_string(), - )); - }; - - let full_node_host = if let Some(ref host) = self.full_node_host { - Some(host) - } else if let Some(ref operator_config) = operator_config { - operator_config.full_node_host.as_ref() - } else { - None - }; - - Ok(( - validator_network_public_key, - full_node_network_public_key, - validator_host, - full_node_host, - )) - } -} - -#[derive(Copy, Clone, Debug, Serialize)] -pub enum StakePoolType { - Direct, - StakingContract, - Vesting, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum StakePoolState { - Active, - Inactive, - PendingActive, - PendingInactive, -} - -#[derive(Debug, Serialize)] -pub struct StakePoolResult { - pub state: StakePoolState, - pub pool_address: AccountAddress, - pub operator_address: AccountAddress, - pub voter_address: AccountAddress, - pub pool_type: StakePoolType, - pub total_stake: u64, - pub commission_percentage: u64, - pub commission_not_yet_unlocked: u64, - pub lockup_expiration_utc_time: DateTime, - pub consensus_public_key: String, - pub validator_network_addresses: Vec, - pub fullnode_network_addresses: Vec, - pub epoch_info: EpochInfo, - #[serde(skip_serializing_if = "Option::is_none")] - pub vesting_contract: Option, -} - -/// Show the stake pool -/// -/// Retrieves the associated stake pool from the multiple types for the given owner address -#[derive(Parser)] -pub struct GetStakePool { - /// The owner address of the stake pool - #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] - pub(crate) owner_address: AccountAddress, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, -} - -#[async_trait] -impl CliCommand> for GetStakePool { - fn command_name(&self) -> &'static str { - "GetStakePool" - } - - async fn execute(mut self) -> CliTypedResult> { - let owner_address = self.owner_address; - let client = &self.rest_options.client(&self.profile_options)?; - get_stake_pools(client, owner_address).await - } -} - -#[derive(Debug, Serialize)] -pub struct StakePoolPerformance { - current_epoch_successful_proposals: u64, - current_epoch_failed_proposals: u64, - previous_epoch_rewards: Vec, - epoch_info: EpochInfo, -} - -/// Show staking performance of the given staking pool -#[derive(Parser)] -pub struct GetPerformance { - #[clap(flatten)] - pub(crate) pool_address_args: PoolAddressArgs, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, -} - -#[async_trait] -impl CliCommand for GetPerformance { - fn command_name(&self) -> &'static str { - "GetPerformance" - } - - async fn execute(mut self) -> CliTypedResult { - let client = &self.rest_options.client(&self.profile_options)?; - let pool_address = self.pool_address_args.pool_address; - let validator_set = &client - .get_account_resource_bcs::(CORE_CODE_ADDRESS, "0x1::stake::ValidatorSet") - .await? - .into_inner(); - - let mut current_epoch_successful_proposals = 0; - let mut current_epoch_failed_proposals = 0; - let state = get_stake_pool_state(validator_set, &pool_address); - if state == StakePoolState::Active || state == StakePoolState::PendingInactive { - let validator_config = client - .get_account_resource_bcs::( - pool_address, - "0x1::stake::ValidatorConfig", - ) - .await? - .into_inner(); - let validator_performances = &client - .get_account_resource_bcs::( - CORE_CODE_ADDRESS, - "0x1::stake::ValidatorPerformance", - ) - .await? - .into_inner(); - let validator_index = validator_config.validator_index as usize; - current_epoch_successful_proposals = - validator_performances.validators[validator_index].successful_proposals; - current_epoch_failed_proposals = - validator_performances.validators[validator_index].failed_proposals; - }; - - let previous_epoch_rewards = client - .get_account_events( - pool_address, - "0x1::stake::StakePool", - "distribute_rewards_events", - Some(0), - Some(10), - ) - .await - .unwrap() - .into_inner() - .into_iter() - .map(|e: VersionedEvent| { - e.data - .get("rewards_amount") - .unwrap() - .as_str() - .unwrap() - .into() - }) - .collect(); - - Ok(StakePoolPerformance { - current_epoch_successful_proposals, - current_epoch_failed_proposals, - previous_epoch_rewards, - epoch_info: get_epoch_info(client).await?, - }) - } -} - -/// Retrieves all stake pools associated with an account -pub async fn get_stake_pools( - client: &Client, - owner_address: AccountAddress, -) -> CliTypedResult> { - let epoch_info = get_epoch_info(client).await?; - let validator_set = &client - .get_account_resource_bcs::(CORE_CODE_ADDRESS, "0x1::stake::ValidatorSet") - .await? - .into_inner(); - let mut stake_pool_results: Vec = vec![]; - // Add direct stake pool if any. - let direct_stake_pool = get_stake_pool_info( - client, - owner_address, - StakePoolType::Direct, - 0, - 0, - epoch_info.clone(), - validator_set, - None, - ) - .await; - if let Ok(direct_stake_pool) = direct_stake_pool { - stake_pool_results.push(direct_stake_pool); - }; - - // Fetch all stake pools managed via staking contracts. - let staking_contract_pools = get_staking_contract_pools( - client, - owner_address, - StakePoolType::StakingContract, - epoch_info.clone(), - validator_set, - None, - ) - .await; - if let Ok(mut staking_contract_pools) = staking_contract_pools { - stake_pool_results.append(&mut staking_contract_pools); - }; - - // Fetch all stake pools managed via employee vesting accounts. - let vesting_admin_store = client - .get_account_resource_bcs::(owner_address, "0x1::vesting::AdminStore") - .await; - if let Ok(vesting_admin_store) = vesting_admin_store { - let vesting_contracts = vesting_admin_store.into_inner().vesting_contracts; - for vesting_contract in vesting_contracts { - let mut staking_contract_pools = get_staking_contract_pools( - client, - vesting_contract, - StakePoolType::Vesting, - epoch_info.clone(), - validator_set, - Some(vesting_contract), - ) - .await - .unwrap(); - stake_pool_results.append(&mut staking_contract_pools); - } - }; - - Ok(stake_pool_results) -} - -/// Retrieve 0x1::staking_contract related pools -pub async fn get_staking_contract_pools( - client: &Client, - staker_address: AccountAddress, - pool_type: StakePoolType, - epoch_info: EpochInfo, - validator_set: &ValidatorSet, - vesting_contract: Option, -) -> CliTypedResult> { - let mut stake_pool_results: Vec = vec![]; - let staking_contract_store = client - .get_account_resource_bcs::( - staker_address, - "0x1::staking_contract::Store", - ) - .await?; - let staking_contracts = staking_contract_store.into_inner().staking_contracts; - for staking_contract in staking_contracts { - let stake_pool_address = get_stake_pool_info( - client, - staking_contract.value.pool_address, - pool_type, - staking_contract.value.principal, - staking_contract.value.commission_percentage, - epoch_info.clone(), - validator_set, - vesting_contract, - ) - .await - .unwrap(); - stake_pool_results.push(stake_pool_address); - } - Ok(stake_pool_results) -} - -pub async fn get_stake_pool_info( - client: &Client, - pool_address: AccountAddress, - pool_type: StakePoolType, - principal: u64, - commission_percentage: u64, - epoch_info: EpochInfo, - validator_set: &ValidatorSet, - vesting_contract: Option, -) -> CliTypedResult { - let stake_pool = client - .get_account_resource_bcs::(pool_address, "0x1::stake::StakePool") - .await? - .into_inner(); - let validator_config = client - .get_account_resource_bcs::(pool_address, "0x1::stake::ValidatorConfig") - .await? - .into_inner(); - let total_stake = stake_pool.get_total_staked_amount(); - let commission_not_yet_unlocked = (total_stake - principal) * commission_percentage / 100; - let state = get_stake_pool_state(validator_set, &pool_address); - - let consensus_public_key = if validator_config.consensus_public_key.is_empty() { - "".into() - } else { - PublicKey::try_from(&validator_config.consensus_public_key[..]) - .unwrap() - .to_encoded_string() - .unwrap() - }; - Ok(StakePoolResult { - state, - pool_address, - operator_address: stake_pool.operator_address, - voter_address: stake_pool.delegated_voter, - pool_type, - total_stake, - commission_percentage, - commission_not_yet_unlocked, - lockup_expiration_utc_time: Time::new_seconds(stake_pool.locked_until_secs).utc_time, - consensus_public_key, - validator_network_addresses: validator_config - .validator_network_addresses() - .unwrap_or_default(), - fullnode_network_addresses: validator_config - .fullnode_network_addresses() - .unwrap_or_default(), - epoch_info, - vesting_contract, - }) -} - -fn get_stake_pool_state( - validator_set: &ValidatorSet, - pool_address: &AccountAddress, -) -> StakePoolState { - if validator_set.active_validators().contains(pool_address) { - StakePoolState::Active - } else if validator_set - .pending_active_validators() - .contains(pool_address) - { - StakePoolState::PendingActive - } else if validator_set - .pending_inactive_validators() - .contains(pool_address) - { - StakePoolState::PendingInactive - } else { - StakePoolState::Inactive - } -} - -/// Register the current account as a validator -/// -/// This will create a new stake pool for the given account. The voter and operator fields will be -/// defaulted to the stake pool account if not provided. -#[derive(Parser)] -pub struct InitializeValidator { - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) operator_config_file_args: OperatorConfigFileArgs, - #[clap(flatten)] - pub(crate) validator_consensus_key_args: ValidatorConsensusKeyArgs, - #[clap(flatten)] - pub(crate) validator_network_addresses_args: ValidatorNetworkAddressesArgs, -} - -#[async_trait] -impl CliCommand for InitializeValidator { - fn command_name(&self) -> &'static str { - "InitializeValidator" - } - - async fn execute(mut self) -> CliTypedResult { - let operator_config = self.operator_config_file_args.load()?; - let consensus_public_key = self - .validator_consensus_key_args - .get_consensus_public_key(&operator_config)?; - let consensus_proof_of_possession = self - .validator_consensus_key_args - .get_consensus_proof_of_possession(&operator_config)?; - let ( - validator_network_public_key, - full_node_network_public_key, - validator_host, - full_node_host, - ) = self - .validator_network_addresses_args - .get_network_configs(&operator_config)?; - let validator_network_addresses = - vec![validator_host.as_network_address(validator_network_public_key)?]; - let full_node_network_addresses = - match (full_node_host.as_ref(), full_node_network_public_key) { - (Some(host), Some(public_key)) => vec![host.as_network_address(public_key)?], - (None, None) => vec![], - _ => { - return Err(CliError::CommandArgumentError( - "If specifying fullnode addresses, both host and public key are required." - .to_string(), - )) - }, - }; - - self.txn_options - .submit_transaction(aptos_stdlib::stake_initialize_validator( - consensus_public_key.to_bytes().to_vec(), - consensus_proof_of_possession.to_bytes().to_vec(), - // BCS encode, so that we can hide the original type - bcs::to_bytes(&validator_network_addresses)?, - bcs::to_bytes(&full_node_network_addresses)?, - )) - .await - .map(|inner| inner.into()) - } -} - -/// Arguments used for operator of the staking pool -#[derive(Parser)] -pub struct OperatorArgs { - #[clap(flatten)] - pub(crate) pool_address_args: OptionalPoolAddressArgs, -} - -impl OperatorArgs { - fn address_fallback_to_profile( - &self, - profile_options: &ProfileOptions, - ) -> CliTypedResult { - if let Some(address) = self.pool_address_args.pool_address { - Ok(address) - } else { - profile_options.account_address() - } - } - - fn address_fallback_to_txn( - &self, - transaction_options: &TransactionOptions, - ) -> CliTypedResult { - if let Some(address) = self.pool_address_args.pool_address { - Ok(address) - } else { - transaction_options.sender_address() - } - } -} - -/// Join the validator set after meeting staking requirements -/// -/// Joining the validator set requires sufficient stake. Once the transaction -/// succeeds, you will join the validator set in the next epoch. -#[derive(Parser)] -pub struct JoinValidatorSet { - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) operator_args: OperatorArgs, -} - -#[async_trait] -impl CliCommand for JoinValidatorSet { - fn command_name(&self) -> &'static str { - "JoinValidatorSet" - } - - async fn execute(mut self) -> CliTypedResult { - let address = self - .operator_args - .address_fallback_to_txn(&self.txn_options)?; - - self.txn_options - .submit_transaction(aptos_stdlib::stake_join_validator_set(address)) - .await - .map(|inner| inner.into()) - } -} - -/// Leave the validator set -/// -/// Leaving the validator set will require you to have unlocked and withdrawn all stake. After this -/// transaction is successful, you will leave the validator set in the next epoch. -#[derive(Parser)] -pub struct LeaveValidatorSet { - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) operator_args: OperatorArgs, -} - -#[async_trait] -impl CliCommand for LeaveValidatorSet { - fn command_name(&self) -> &'static str { - "LeaveValidatorSet" - } - - async fn execute(mut self) -> CliTypedResult { - let address = self - .operator_args - .address_fallback_to_txn(&self.txn_options)?; - - self.txn_options - .submit_transaction(aptos_stdlib::stake_leave_validator_set(address)) - .await - .map(|inner| inner.into()) - } -} - -/// Show validator stake information for a specific validator -/// -/// This will show information about a specific validator, given its -/// `--pool-address`. -#[derive(Parser)] -pub struct ShowValidatorStake { - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) operator_args: OperatorArgs, -} - -#[async_trait] -impl CliCommand for ShowValidatorStake { - fn command_name(&self) -> &'static str { - "ShowValidatorStake" - } - - async fn execute(mut self) -> CliTypedResult { - let client = self.rest_options.client(&self.profile_options)?; - let address = self - .operator_args - .address_fallback_to_profile(&self.profile_options)?; - let response = client - .get_resource(address, "0x1::stake::StakePool") - .await?; - Ok(response.into_inner()) - } -} - -/// Show validator configuration for a specific validator -/// -/// This will show information about a specific validator, given its -/// `--pool-address`. -#[derive(Parser)] -pub struct ShowValidatorConfig { - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) operator_args: OperatorArgs, -} - -#[async_trait] -impl CliCommand for ShowValidatorConfig { - fn command_name(&self) -> &'static str { - "ShowValidatorConfig" - } - - async fn execute(mut self) -> CliTypedResult { - let client = self.rest_options.client(&self.profile_options)?; - let address = self - .operator_args - .address_fallback_to_profile(&self.profile_options)?; - let validator_config: ValidatorConfig = client - .get_account_resource_bcs(address, "0x1::stake::ValidatorConfig") - .await? - .into_inner(); - Ok((&validator_config) - .try_into() - .map_err(|err| CliError::BCS("Validator config", err))?) - } -} - -/// Show validator details of the validator set -/// -/// This will show information about the validators including their voting power, addresses, and -/// public keys. -#[derive(Parser)] -pub struct ShowValidatorSet { - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, -} - -#[async_trait] -impl CliCommand for ShowValidatorSet { - fn command_name(&self) -> &'static str { - "ShowValidatorSet" - } - - async fn execute(mut self) -> CliTypedResult { - let client = self.rest_options.client(&self.profile_options)?; - let validator_set: ValidatorSet = client - .get_account_resource_bcs(CORE_CODE_ADDRESS, "0x1::stake::ValidatorSet") - .await? - .into_inner(); - - ValidatorSetSummary::try_from(&validator_set) - .map_err(|err| CliError::BCS("Validator Set", err)) - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct ValidatorSetSummary { - pub scheme: ConsensusScheme, - pub active_validators: Vec, - pub pending_inactive: Vec, - pub pending_active: Vec, - pub total_voting_power: u128, - pub total_joining_power: u128, -} - -impl TryFrom<&ValidatorSet> for ValidatorSetSummary { - type Error = bcs::Error; - - fn try_from(set: &ValidatorSet) -> Result { - Ok(ValidatorSetSummary { - scheme: set.scheme, - active_validators: set - .active_validators - .iter() - .filter_map(|validator| validator.try_into().ok()) - .collect(), - pending_inactive: set - .pending_inactive - .iter() - .filter_map(|validator| validator.try_into().ok()) - .collect(), - pending_active: set - .pending_active - .iter() - .filter_map(|validator| validator.try_into().ok()) - .collect(), - total_voting_power: set.total_voting_power, - total_joining_power: set.total_joining_power, - }) - } -} - -impl From<&ValidatorSetSummary> for ValidatorSet { - fn from(summary: &ValidatorSetSummary) -> Self { - ValidatorSet { - scheme: summary.scheme, - active_validators: summary - .active_validators - .iter() - .map(|validator| validator.into()) - .collect(), - pending_inactive: summary - .pending_inactive - .iter() - .map(|validator| validator.into()) - .collect(), - pending_active: summary - .pending_active - .iter() - .map(|validator| validator.into()) - .collect(), - total_voting_power: summary.total_voting_power, - total_joining_power: summary.total_joining_power, - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct ValidatorInfoSummary { - // The validator's account address. AccountAddresses are initially derived from the account - // auth pubkey; however, the auth key can be rotated, so one should not rely on this - // initial property. - pub account_address: AccountAddress, - // Voting power of this validator - consensus_voting_power: u64, - // Validator config - config: ValidatorConfigSummary, -} - -impl TryFrom<&ValidatorInfo> for ValidatorInfoSummary { - type Error = bcs::Error; - - fn try_from(info: &ValidatorInfo) -> Result { - let config = info.config(); - let config = ValidatorConfig { - consensus_public_key: config.consensus_public_key.to_bytes().to_vec(), - validator_network_addresses: config.validator_network_addresses.clone(), - fullnode_network_addresses: config.fullnode_network_addresses.clone(), - validator_index: config.validator_index, - }; - Ok(ValidatorInfoSummary { - account_address: info.account_address, - consensus_voting_power: info.consensus_voting_power(), - config: ValidatorConfigSummary::try_from(&config)?, - }) - } -} - -impl From<&ValidatorInfoSummary> for ValidatorInfo { - fn from(summary: &ValidatorInfoSummary) -> Self { - let config = &summary.config; - ValidatorInfo::new( - summary.account_address, - summary.consensus_voting_power, - aptos_types::validator_config::ValidatorConfig::new( - PublicKey::from_encoded_string(&config.consensus_public_key).unwrap(), - bcs::to_bytes(&config.validator_network_addresses).unwrap(), - bcs::to_bytes(&config.fullnode_network_addresses).unwrap(), - config.validator_index, - ), - ) - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] -pub struct ValidatorConfig { - pub consensus_public_key: Vec, - pub validator_network_addresses: Vec, - pub fullnode_network_addresses: Vec, - pub validator_index: u64, -} - -impl ValidatorConfig { - pub fn new( - consensus_public_key: Vec, - validator_network_addresses: Vec, - fullnode_network_addresses: Vec, - validator_index: u64, - ) -> Self { - ValidatorConfig { - consensus_public_key, - validator_network_addresses, - fullnode_network_addresses, - validator_index, - } - } - - pub fn fullnode_network_addresses(&self) -> Result, bcs::Error> { - bcs::from_bytes(&self.fullnode_network_addresses) - } - - pub fn validator_network_addresses(&self) -> Result, bcs::Error> { - bcs::from_bytes(&self.validator_network_addresses) - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct ValidatorConfigSummary { - pub consensus_public_key: String, - /// This is an bcs serialized `Vec` - pub validator_network_addresses: Vec, - /// This is an bcs serialized `Vec` - pub fullnode_network_addresses: Vec, - pub validator_index: u64, -} - -impl TryFrom<&ValidatorConfig> for ValidatorConfigSummary { - type Error = bcs::Error; - - fn try_from(config: &ValidatorConfig) -> Result { - let consensus_public_key = if config.consensus_public_key.is_empty() { - "".into() - } else { - PublicKey::try_from(&config.consensus_public_key[..]) - .unwrap() - .to_encoded_string() - .unwrap() - }; - Ok(ValidatorConfigSummary { - consensus_public_key, - // TODO: We should handle if some of these are not parsable - validator_network_addresses: config.validator_network_addresses()?, - fullnode_network_addresses: config.fullnode_network_addresses()?, - validator_index: config.validator_index, - }) - } -} - -impl From<&ValidatorConfigSummary> for ValidatorConfig { - fn from(summary: &ValidatorConfigSummary) -> Self { - let consensus_public_key = if summary.consensus_public_key.is_empty() { - vec![] - } else { - summary.consensus_public_key.as_bytes().to_vec() - }; - ValidatorConfig { - consensus_public_key, - validator_network_addresses: bcs::to_bytes(&summary.validator_network_addresses) - .unwrap(), - fullnode_network_addresses: bcs::to_bytes(&summary.fullnode_network_addresses).unwrap(), - validator_index: summary.validator_index, - } - } -} - -const MAX_WAIT_S: u64 = 30; -const WAIT_INTERVAL_MS: u64 = 100; -const TESTNET_FOLDER: &str = "testnet"; - -/// Run local testnet -/// -/// This local testnet will run it's own Genesis and run as a single node -/// network locally. Optionally, a faucet can be added for minting APT coins. -#[derive(Parser)] -pub struct RunLocalTestnet { - /// An overridable config template for the test node - /// - /// If provided, the config will be used, and any needed configuration for the local testnet - /// will override the config's values - #[clap(long, parse(from_os_str))] - config_path: Option, - - /// The directory to save all files for the node - /// - /// Defaults to .aptos/testnet - #[clap(long, parse(from_os_str))] - test_dir: Option, - - /// Random seed for key generation in test mode - /// - /// This allows you to have deterministic keys for testing - #[clap(long, parse(try_from_str = FromHex::from_hex))] - seed: Option<[u8; 32]>, - - /// Clean the state and start with a new chain at genesis - /// - /// This will wipe the aptosdb in `test-dir` to remove any incompatible changes, and start - /// the chain fresh. Note, that you will need to publish the module again and distribute funds - /// from the faucet accordingly - #[clap(long)] - force_restart: bool, - - /// Run a faucet alongside the node - /// - /// Allows you to run a faucet alongside the node to create and fund accounts for testing - #[clap(long)] - with_faucet: bool, - - /// Port to run the faucet on - /// - /// When running, you'll be able to use the faucet at `http://localhost:/mint` e.g. - /// `http//localhost:8080/mint` - #[clap(long, default_value = "8081")] - faucet_port: u16, - - /// Disable the delegation of faucet minting to a dedicated account - #[clap(long)] - do_not_delegate: bool, - - #[clap(flatten)] - prompt_options: PromptOptions, -} - -#[async_trait] -impl CliCommand<()> for RunLocalTestnet { - fn command_name(&self) -> &'static str { - "RunLocalTestnet" - } - - async fn execute(mut self) -> CliTypedResult<()> { - let rng = self - .seed - .map(StdRng::from_seed) - .unwrap_or_else(StdRng::from_entropy); - - let global_config = GlobalConfig::load()?; - let test_dir = match self.test_dir { - Some(test_dir) => test_dir, - None => global_config - .get_config_location(ConfigSearchMode::CurrentDirAndParents)? - .join(TESTNET_FOLDER), - }; - - // Remove the current test directory and start with a new node - if self.force_restart && test_dir.exists() { - prompt_yes_with_override( - "Are you sure you want to delete the existing chain?", - self.prompt_options, - )?; - std::fs::remove_dir_all(test_dir.as_path()).map_err(|err| { - CliError::IO(format!("Failed to delete {}", test_dir.display()), err) - })?; - } - - // Spawn the node in a separate thread - let config_path = self.config_path.clone(); - let test_dir_copy = test_dir.clone(); - let node_thread_handle = thread::spawn(move || { - let result = aptos_node::setup_test_environment_and_start_node( - config_path, - Some(test_dir_copy), - false, - false, - aptos_cached_packages::head_release_bundle(), - rng, - ); - eprintln!("Node stopped unexpectedly {:#?}", result); - }); - - // Run faucet if selected - let maybe_faucet_future = if self.with_faucet { - let max_wait = Duration::from_secs(MAX_WAIT_S); - let wait_interval = Duration::from_millis(WAIT_INTERVAL_MS); - - // Load the config to get the rest port - let config_path = test_dir.join("0").join("node.yaml"); - - // We have to wait for the node to be configured above in the other thread - let mut config = None; - let start = Instant::now(); - while start.elapsed() < max_wait { - if let Ok(loaded_config) = NodeConfig::load_from_path(&config_path) { - config = Some(loaded_config); - break; - } - tokio::time::sleep(wait_interval).await; - } - - // Retrieve the port from the local node - let port = if let Some(config) = config { - config.api.address.port() - } else { - return Err(CliError::UnexpectedError( - "Failed to find node configuration to start faucet".to_string(), - )); - }; - - // Check that the REST API is ready - let rest_url = Url::parse(&format!("http://localhost:{}", port)).map_err(|err| { - CliError::UnexpectedError(format!("Failed to parse localhost URL {}", err)) - })?; - let rest_client = aptos_rest_client::Client::new(rest_url.clone()); - let start = Instant::now(); - let mut started_successfully = false; - - while start.elapsed() < max_wait { - if rest_client.get_index().await.is_ok() { - started_successfully = true; - break; - } - tokio::time::sleep(wait_interval).await - } - - if !started_successfully { - return Err(CliError::UnexpectedError(format!( - "Local node at {} did not start up before faucet", - rest_url - ))); - } - - // Build the config for the faucet service. - let faucet_config = RunConfig::build_for_cli( - rest_url, - self.faucet_port, - FunderKeyEnum::KeyFile(test_dir.join("mint.key")), - self.do_not_delegate, - None, - ); - - // Start the faucet - Some(faucet_config.run().map(|result| { - eprintln!("Faucet stopped unexpectedly {:#?}", result); - })) - } else { - None - }; - - // Collect futures that should never end. - let mut futures: Vec + Send>>> = Vec::new(); - - // This future just waits for the node thread. - let node_future = async move { - loop { - if node_thread_handle.is_finished() { - return; - } - tokio::time::sleep(Duration::from_millis(500)).await; - } - }; - - // Wait for all the futures. We should never get past this point unless - // something goes wrong or the user signals for the process to end. - futures.push(Box::pin(node_future)); - if let Some(faucet_future) = maybe_faucet_future { - futures.push(Box::pin(faucet_future)); - } - futures::future::select_all(futures).await; - - Err(CliError::UnexpectedError( - "One of the components stopped unexpectedly".to_string(), - )) - } -} - -/// Update consensus key for the validator node -/// -/// This will take effect in the next epoch -#[derive(Parser)] -pub struct UpdateConsensusKey { - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) operator_args: OperatorArgs, - #[clap(flatten)] - pub(crate) operator_config_file_args: OperatorConfigFileArgs, - #[clap(flatten)] - pub(crate) validator_consensus_key_args: ValidatorConsensusKeyArgs, -} - -#[async_trait] -impl CliCommand for UpdateConsensusKey { - fn command_name(&self) -> &'static str { - "UpdateConsensusKey" - } - - async fn execute(mut self) -> CliTypedResult { - let address = self - .operator_args - .address_fallback_to_txn(&self.txn_options)?; - - let operator_config = self.operator_config_file_args.load()?; - let consensus_public_key = self - .validator_consensus_key_args - .get_consensus_public_key(&operator_config)?; - let consensus_proof_of_possession = self - .validator_consensus_key_args - .get_consensus_proof_of_possession(&operator_config)?; - self.txn_options - .submit_transaction(aptos_stdlib::stake_rotate_consensus_key( - address, - consensus_public_key.to_bytes().to_vec(), - consensus_proof_of_possession.to_bytes().to_vec(), - )) - .await - .map(|inner| inner.into()) - } -} - -/// Update the current validator's network and fullnode network addresses -/// -/// This will take effect in the next epoch -#[derive(Parser)] -pub struct UpdateValidatorNetworkAddresses { - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) operator_args: OperatorArgs, - #[clap(flatten)] - pub(crate) operator_config_file_args: OperatorConfigFileArgs, - #[clap(flatten)] - pub(crate) validator_network_addresses_args: ValidatorNetworkAddressesArgs, -} - -#[async_trait] -impl CliCommand for UpdateValidatorNetworkAddresses { - fn command_name(&self) -> &'static str { - "UpdateValidatorNetworkAddresses" - } - - async fn execute(mut self) -> CliTypedResult { - let address = self - .operator_args - .address_fallback_to_txn(&self.txn_options)?; - - let validator_config = self.operator_config_file_args.load()?; - let ( - validator_network_public_key, - full_node_network_public_key, - validator_host, - full_node_host, - ) = self - .validator_network_addresses_args - .get_network_configs(&validator_config)?; - let validator_network_addresses = - vec![validator_host.as_network_address(validator_network_public_key)?]; - let full_node_network_addresses = - match (full_node_host.as_ref(), full_node_network_public_key) { - (Some(host), Some(public_key)) => vec![host.as_network_address(public_key)?], - (None, None) => vec![], - _ => { - return Err(CliError::CommandArgumentError( - "If specifying fullnode addresses, both host and public key are required." - .to_string(), - )) - }, - }; - - self.txn_options - .submit_transaction(aptos_stdlib::stake_update_network_and_fullnode_addresses( - address, - // BCS encode, so that we can hide the original type - bcs::to_bytes(&validator_network_addresses)?, - bcs::to_bytes(&full_node_network_addresses)?, - )) - .await - .map(|inner| inner.into()) - } -} - -/// Analyze the performance of one or more validators -#[derive(Parser)] -pub struct AnalyzeValidatorPerformance { - /// First epoch to analyze - /// - /// Defaults to the first epoch - #[clap(long, default_value = "-2")] - pub start_epoch: i64, - - /// Last epoch to analyze - /// - /// Defaults to the latest epoch - #[clap(long)] - pub end_epoch: Option, - - /// Analyze mode for the validator: [All, DetailedEpochTable, ValidatorHealthOverTime, NetworkHealthOverTime] - #[clap(arg_enum, long)] - pub(crate) analyze_mode: AnalyzeMode, - - /// Filter of stake pool addresses to analyze - /// - /// Defaults to all stake pool addresses - #[clap(long, multiple_values = true, parse(try_from_str=crate::common::types::load_account_arg))] - pub pool_addresses: Vec, - - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, -} - -#[derive(PartialEq, Eq, clap::ArgEnum, Clone)] -pub enum AnalyzeMode { - /// Print all other modes simultaneously - All, - /// For each epoch, print a detailed table containing performance - /// of each of the validators. - DetailedEpochTable, - /// For each validator, summarize it's performance in an epoch into - /// one of the predefined reliability buckets, - /// and prints it's performance across epochs. - ValidatorHealthOverTime, - /// For each epoch summarize how many validators were in - /// each of the reliability buckets. - NetworkHealthOverTime, -} - -#[async_trait] -impl CliCommand<()> for AnalyzeValidatorPerformance { - fn command_name(&self) -> &'static str { - "AnalyzeValidatorPerformance" - } - - async fn execute(mut self) -> CliTypedResult<()> { - let client = self.rest_options.client(&self.profile_options)?; - - let epochs = - FetchMetadata::fetch_new_block_events(&client, Some(self.start_epoch), self.end_epoch) - .await?; - let mut stats = HashMap::new(); - - let print_detailed = self.analyze_mode == AnalyzeMode::DetailedEpochTable - || self.analyze_mode == AnalyzeMode::All; - for epoch_info in epochs { - let mut epoch_stats = - AnalyzeValidators::analyze(&epoch_info.blocks, &epoch_info.validators); - if !self.pool_addresses.is_empty() { - let mut filtered_stats: HashMap = HashMap::new(); - for pool_address in &self.pool_addresses { - filtered_stats.insert( - *pool_address, - *epoch_stats.validator_stats.get(pool_address).unwrap(), - ); - } - epoch_stats.validator_stats = filtered_stats; - } - if print_detailed { - println!( - "Detailed table for {}epoch {}:", - if epoch_info.partial { "partial " } else { "" }, - epoch_info.epoch - ); - AnalyzeValidators::print_detailed_epoch_table( - &epoch_stats, - Some(( - "voting_power", - &epoch_info - .validators - .iter() - .map(|v| (v.address, v.voting_power.to_string())) - .collect::>(), - )), - true, - ); - } - if !epoch_info.partial { - stats.insert(epoch_info.epoch, epoch_stats); - } - } - - if stats.is_empty() { - println!("No data found for given input"); - return Ok(()); - } - let total_stats = stats.values().cloned().reduce(|a, b| a + b).unwrap(); - if print_detailed { - println!( - "Detailed table for all epochs [{}, {}]:", - stats.keys().min().unwrap(), - stats.keys().max().unwrap() - ); - AnalyzeValidators::print_detailed_epoch_table(&total_stats, None, true); - } - let all_validators: Vec<_> = total_stats.validator_stats.keys().cloned().collect(); - if self.analyze_mode == AnalyzeMode::ValidatorHealthOverTime - || self.analyze_mode == AnalyzeMode::All - { - println!( - "Validator health over epochs [{}, {}]:", - stats.keys().min().unwrap(), - stats.keys().max().unwrap() - ); - AnalyzeValidators::print_validator_health_over_time(&stats, &all_validators, None); - } - if self.analyze_mode == AnalyzeMode::NetworkHealthOverTime - || self.analyze_mode == AnalyzeMode::All - { - println!( - "Network health over epochs [{}, {}]:", - stats.keys().min().unwrap(), - stats.keys().max().unwrap() - ); - AnalyzeValidators::print_network_health_over_time(&stats, &all_validators); - } - Ok(()) - } -} - -/// Bootstrap AptosDB from a backup -/// -/// Enables users to load from a backup to catch their node's DB up to a known state. -#[derive(Parser)] -pub struct BootstrapDbFromBackup { - /// Config file for the source backup - /// - /// This file configures if we should use local files or cloud storage, and how to access - /// the backup. - #[clap(long, parse(from_os_str))] - config_path: PathBuf, - - /// Target database directory - /// - /// The directory to create the AptosDB with snapshots and transactions from the backup. - /// The data folder can later be used to start an Aptos node. e.g. /opt/aptos/data/db - #[clap(long = "target-db-dir", parse(from_os_str))] - pub db_dir: PathBuf, - - #[clap(flatten)] - pub metadata_cache_opt: MetadataCacheOpt, - - #[clap(flatten)] - pub concurrent_downloads: ConcurrentDownloadsOpt, - - #[clap(flatten)] - pub replay_concurrency_level: ReplayConcurrencyLevelOpt, -} - -#[async_trait] -impl CliCommand<()> for BootstrapDbFromBackup { - fn command_name(&self) -> &'static str { - "BootstrapDbFromBackup" - } - - async fn execute(self) -> CliTypedResult<()> { - let opt = RestoreCoordinatorOpt { - metadata_cache_opt: self.metadata_cache_opt, - replay_all: false, - ledger_history_start_version: None, - skip_epoch_endings: false, - }; - let global_opt = GlobalRestoreOpt { - dry_run: false, - db_dir: Some(self.db_dir), - target_version: None, - trusted_waypoints: Default::default(), - rocksdb_opt: RocksdbOpt::default(), - concurrent_downloads: self.concurrent_downloads, - replay_concurrency_level: self.replay_concurrency_level, - } - .try_into()?; - let storage = Arc::new(CommandAdapter::new( - CommandAdapterConfig::load_from_file(&self.config_path).await?, - )); - - // hack: get around this error, related to use of `async_trait`: - // error: higher-ranked lifetime error - // ... - // = note: could not prove for<'r, 's> Pin>>>: CoerceUnsized> + std::marker::Send + 's)>>> - tokio::task::spawn_blocking(|| { - let runtime = tokio::runtime::Runtime::new().unwrap(); - runtime.block_on(RestoreCoordinator::new(opt, global_opt, storage).run()) - }) - .await - .unwrap()?; - Ok(()) - } -} - -/// Checks the network connectivity of a node -/// -/// Checks network connectivity by dialing the node and attempting -/// to establish a connection with a noise handshake. -#[derive(Parser)] -pub struct CheckNetworkConnectivity { - /// `NetworkAddress` of remote server interface. - /// Examples include: - /// - `/dns/example.com/tcp/6180/noise-ik//handshake/1` - /// - `/ip4//tcp/6182/noise-ik//handshake/0` - #[clap(long, value_parser = validate_address)] - pub address: NetworkAddress, - - /// `ChainId` of remote server. - /// Examples include: - /// - Chain numbers, e.g., `2`, `3` and `25`. - /// - Chain names, e.g., `devnet`, `testnet`, `mainnet` and `testing` (for local test networks). - #[clap(long)] - pub chain_id: ChainId, - - #[clap(flatten)] - pub handshake_args: HandshakeArgs, -} - -#[async_trait] -impl CliCommand for CheckNetworkConnectivity { - fn command_name(&self) -> &'static str { - "CheckNetworkConnectivity" - } - - async fn execute(self) -> CliTypedResult { - // Create the check endpoint args for the checker - let node_address_args = NodeAddressArgs { - address: self.address, - chain_id: self.chain_id, - }; - let check_endpoint_args = CheckEndpointArgs { - node_address_args, - handshake_args: self.handshake_args, - }; - - // Check the endpoint - aptos_network_checker::check_endpoint(&check_endpoint_args, None) - .await - .map_err(|error| CliError::UnexpectedError(error.to_string())) - } -} - -/// Show epoch information -/// -/// Displays the current epoch, the epoch length, and the estimated time of the next epoch -#[derive(Parser)] -pub struct ShowEpochInfo { - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, -} - -#[async_trait] -impl CliCommand for ShowEpochInfo { - fn command_name(&self) -> &'static str { - "ShowEpochInfo" - } - - async fn execute(self) -> CliTypedResult { - let client = &self.rest_options.client(&self.profile_options)?; - get_epoch_info(client).await - } -} - -async fn get_epoch_info(client: &Client) -> CliTypedResult { - let (block_resource, state): (BlockResource, State) = client - .get_account_resource_bcs(CORE_CODE_ADDRESS, "0x1::block::BlockResource") - .await? - .into_parts(); - let reconfig_resource: ConfigurationResource = client - .get_account_resource_at_version_bcs( - CORE_CODE_ADDRESS, - "0x1::reconfiguration::Configuration", - state.version, - ) - .await? - .into_inner(); - - let epoch_interval = block_resource.epoch_interval(); - let epoch_interval_secs = epoch_interval / SECS_TO_MICROSECS; - let last_reconfig = reconfig_resource.last_reconfiguration_time(); - Ok(EpochInfo { - epoch: reconfig_resource.epoch(), - epoch_interval_secs, - current_epoch_start_time: Time::new_micros(last_reconfig), - next_epoch_start_time: Time::new_micros(last_reconfig + epoch_interval), - }) -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EpochInfo { - epoch: u64, - epoch_interval_secs: u64, - current_epoch_start_time: Time, - next_epoch_start_time: Time, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Time { - unix_time: u128, - utc_time: DateTime, -} - -impl Time { - pub fn new(time: Duration) -> Self { - let date_time = - NaiveDateTime::from_timestamp_opt(time.as_secs() as i64, time.subsec_nanos()).unwrap(); - let utc_time = DateTime::from_utc(date_time, Utc); - // TODO: Allow configurable time zone - Self { - unix_time: time.as_micros(), - utc_time, - } - } - - pub fn new_micros(microseconds: u64) -> Self { - Self::new(Duration::from_micros(microseconds)) - } - - pub fn new_seconds(seconds: u64) -> Self { - Self::new(Duration::from_secs(seconds)) - } -} - -#[cfg(test)] -mod tests { - use crate::{CliResult, Tool}; - use clap::Parser; - - // TODO: there have to be cleaner ways to test things. Maybe a CLI test framework? - - #[tokio::test] - // Verifies basic properties about the network connectivity checker - async fn test_check_network_connectivity() { - // Verify the help function works - let args = &["movement", "node", "check-network-connectivity", "--help"]; - let help_message = run_tool_with_args(args).await.unwrap_err(); - assert_contains(help_message, "USAGE:"); // We expect the command to return USAGE info - - // Verify that an invalid address will return an error - let args = &[ - "movement", - "node", - "check-network-connectivity", - "--address", - "invalid-address", - "--chain-id", - "mainnet", - ]; - let error_message = run_tool_with_args(args).await.unwrap_err(); - assert_contains(error_message, "Invalid address"); - - // Verify that an invalid chain-id will return an error - let args = &["movement", "node", "check-network-connectivity", "--address", "/ip4/34.70.116.169/tcp/6182/noise-ik/0x249f3301db104705652e0a0c471b46d13172b2baf14e31f007413f3baee46b0c/handshake/0", "--chain-id", "invalid-chain"]; - let error_message = run_tool_with_args(args).await.unwrap_err(); - assert_contains(error_message, "Invalid value"); - - // Verify that a failure to connect will return a timeout - let args = &["movement", "node", "check-network-connectivity", "--address", "/ip4/31.71.116.169/tcp/0001/noise-ik/0x249f3301db104705652e0a0c471b46d13172b2baf14e31f007413f3baee46b0c/handshake/0", "--chain-id", "testnet"]; - let error_message = run_tool_with_args(args).await.unwrap_err(); - assert_contains(error_message, "Timed out while checking endpoint"); - } - - async fn run_tool_with_args(args: &[&str]) -> CliResult { - let tool: Tool = Tool::try_parse_from(args).map_err(|msg| msg.to_string())?; - tool.execute().await - } - - fn assert_contains(message: String, expected_string: &str) { - if !message.contains(expected_string) { - panic!( - "Expected message to contain {:?}, but it did not! Message: {:?}", - expected_string, message - ); - } - } -} diff --git a/m1/m1-cli/src/op/key.rs b/m1/m1-cli/src/op/key.rs deleted file mode 100644 index aac2d31f7..000000000 --- a/m1/m1-cli/src/op/key.rs +++ /dev/null @@ -1,396 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - types::{ - account_address_from_public_key, CliError, CliTypedResult, EncodingOptions, - EncodingType, KeyType, RngArgs, SaveFile, - }, - utils::{ - append_file_extension, check_if_file_exists, generate_vanity_account_ed25519, - write_to_file, - }, - }, - CliCommand, CliResult, -}; -use aptos_config::config::{Peer, PeerRole}; -use aptos_crypto::{bls12381, ed25519, x25519, PrivateKey, ValidCryptoMaterial}; -use aptos_genesis::config::HostAndPort; -use aptos_types::account_address::{ - create_multisig_account_address, from_identity_public_key, AccountAddress, -}; -use async_trait::async_trait; -use clap::{Parser, Subcommand}; -use std::{ - collections::{HashMap, HashSet}, - path::{Path, PathBuf}, -}; - -pub const PUBLIC_KEY_EXTENSION: &str = "pub"; - -/// Tool for generating, inspecting, and interacting with keys -/// -/// This tool allows users to generate and extract related information -/// with all key types used on the Aptos blockchain. -#[derive(Debug, Subcommand)] -pub enum KeyTool { - Generate(GenerateKey), - ExtractPeer(ExtractPeer), -} - -impl KeyTool { - pub async fn execute(self) -> CliResult { - match self { - KeyTool::Generate(tool) => tool.execute_serialized().await, - KeyTool::ExtractPeer(tool) => tool.execute_serialized().await, - } - } -} - -/// Extract full peer information for an upstream peer -/// -/// This command builds a YAML blob that can be copied into a user's network configuration. -/// A host is required to build the network address used for the connection, and the -/// network key is required to identify the peer. -/// -/// A `private-network-key` or `public-network-key` can be given encoded on the command line, or -/// a `private-network-key-file` or a `public-network-key-file` can be given to read from. -/// The `output-file` will be a YAML serialized peer information for use in network config. -#[derive(Debug, Parser)] -pub struct ExtractPeer { - /// Host and port of the full node - /// - /// e.g. 127.0.0.1:6180 or my-awesome-dns.com:6180 - #[clap(long)] - pub(crate) host: HostAndPort, - - #[clap(flatten)] - pub(crate) network_key_input_options: NetworkKeyInputOptions, - #[clap(flatten)] - pub(crate) output_file_options: SaveFile, - #[clap(flatten)] - pub(crate) encoding_options: EncodingOptions, -} - -#[async_trait] -impl CliCommand> for ExtractPeer { - fn command_name(&self) -> &'static str { - "ExtractPeer" - } - - async fn execute(self) -> CliTypedResult> { - // Load key based on public or private - let public_key = self - .network_key_input_options - .extract_public_network_key(self.encoding_options.encoding)?; - - // Check output file exists - self.output_file_options.check_file()?; - - // Build peer info - let peer_id = from_identity_public_key(public_key); - let mut public_keys = HashSet::new(); - public_keys.insert(public_key); - - let address = self.host.as_network_address(public_key).map_err(|err| { - CliError::UnexpectedError(format!("Failed to build network address: {}", err)) - })?; - - let peer = Peer::new(vec![address], public_keys, PeerRole::Upstream); - - let mut map = HashMap::new(); - map.insert(peer_id, peer); - - // Save to file - let yaml = serde_yaml::to_string(&map) - .map_err(|err| CliError::UnexpectedError(err.to_string()))?; - self.output_file_options - .save_to_file("Extracted peer", yaml.as_bytes())?; - Ok(map) - } -} - -#[derive(Debug, Default, Parser)] -pub struct NetworkKeyInputOptions { - /// x25519 Private key input file name - #[clap(long, group = "network_key_input", parse(from_os_str))] - private_network_key_file: Option, - - /// x25519 Private key encoded in a type as shown in `encoding` - #[clap(long, group = "network_key_input")] - private_network_key: Option, - - /// x25519 Public key input file name - #[clap(long, group = "network_key_input", parse(from_os_str))] - public_network_key_file: Option, - - /// x25519 Public key encoded in a type as shown in `encoding` - #[clap(long, group = "network_key_input")] - public_network_key: Option, -} - -impl NetworkKeyInputOptions { - pub fn from_private_key_file(file: PathBuf) -> Self { - Self { - private_network_key_file: Some(file), - private_network_key: None, - public_network_key_file: None, - public_network_key: None, - } - } - - pub fn extract_public_network_key( - self, - encoding: EncodingType, - ) -> CliTypedResult { - // The grouping above prevents there from being more than one, but just in case - match (self.public_network_key, self.public_network_key_file, self.private_network_key, self.private_network_key_file){ - (Some(public_network_key), None, None, None) => encoding.decode_key("--public-network-key", public_network_key.as_bytes().to_vec()), - (None, Some(public_network_key_file),None, None) => encoding.load_key("--public-network-key-file", public_network_key_file.as_path()), - (None, None, Some(private_network_key), None) => { - let private_network_key: x25519::PrivateKey = encoding.decode_key("--private-network-key", private_network_key.as_bytes().to_vec())?; - Ok(private_network_key.public_key()) - }, - (None, None, None, Some(private_network_key_file)) => { - let private_network_key: x25519::PrivateKey = encoding.load_key("--private-network-key-file", private_network_key_file.as_path())?; - Ok(private_network_key.public_key()) - }, - _ => Err(CliError::CommandArgumentError("Must provide exactly one of [--public-network-key, --public-network-key-file, --private-network-key, --private-network-key-file]".to_string())) - } - } -} - -/// Generates a `x25519` or `ed25519` key. -/// -/// This can be used for generating an identity. Two files will be created -/// `output_file` and `output_file.pub`. `output_file` will contain the private -/// key encoded with the `encoding` and `output_file.pub` will contain the public -/// key encoded with the `encoding`. -#[derive(Debug, Parser)] -pub struct GenerateKey { - /// Key type to generate. Must be one of [x25519, ed25519, bls12381] - #[clap(long, default_value_t = KeyType::Ed25519)] - pub(crate) key_type: KeyType, - /// Vanity prefix that resultant account address should start with, e.g. 0xaceface or d00d. Each - /// additional character multiplies by a factor of 16 the computational difficulty associated - /// with generating an address, so try out shorter prefixes first and be prepared to wait for - /// longer ones - #[clap(long)] - pub vanity_prefix: Option, - /// Use this flag when vanity prefix is for a multisig account. This mines a private key for - /// a single signer account that can, as its first transaction, create a multisig account with - /// the given vanity prefix - #[clap(long)] - pub vanity_multisig: bool, - #[clap(flatten)] - pub rng_args: RngArgs, - #[clap(flatten)] - pub(crate) save_params: SaveKey, -} - -#[async_trait] -impl CliCommand> for GenerateKey { - fn command_name(&self) -> &'static str { - "GenerateKey" - } - - async fn execute(self) -> CliTypedResult> { - if self.vanity_prefix.is_some() && !matches!(self.key_type, KeyType::Ed25519) { - return Err(CliError::CommandArgumentError(format!( - "Vanity prefixes are only accepted for {} keys", - KeyType::Ed25519 - ))); - } - if self.vanity_multisig && self.vanity_prefix.is_none() { - return Err(CliError::CommandArgumentError( - "No vanity prefix provided".to_string(), - )); - } - self.save_params.check_key_file()?; - let mut keygen = self.rng_args.key_generator()?; - match self.key_type { - KeyType::X25519 => { - let private_key = keygen.generate_x25519_private_key().map_err(|err| { - CliError::UnexpectedError(format!( - "Failed to convert ed25519 to x25519 {:?}", - err - )) - })?; - self.save_params.save_key(&private_key, "x25519") - }, - KeyType::Ed25519 => { - // If no vanity prefix specified, generate a standard Ed25519 private key. - let private_key = if self.vanity_prefix.is_none() { - keygen.generate_ed25519_private_key() - } else { - // If a vanity prefix is specified, generate vanity Ed25519 account from it. - generate_vanity_account_ed25519( - self.vanity_prefix.clone().unwrap().as_str(), - self.vanity_multisig, - )? - }; - // Store CLI result from key save operation, to append vanity address(es) if needed. - let mut result_map = self.save_params.save_key(&private_key, "ed25519").unwrap(); - if self.vanity_prefix.is_some() { - let account_address = account_address_from_public_key( - &ed25519::Ed25519PublicKey::from(&private_key), - ); - // Store account address in a PathBuf so it can be displayed in CLI result. - result_map.insert( - "Account Address:", - PathBuf::from(account_address.to_hex_literal()), - ); - if self.vanity_multisig { - let multisig_account_address = - create_multisig_account_address(account_address, 0); - result_map.insert( - "Multisig Account Address:", - PathBuf::from(multisig_account_address.to_hex_literal()), - ); - } - } - return Ok(result_map); - }, - KeyType::Bls12381 => { - let private_key = keygen.generate_bls12381_private_key(); - self.save_params.save_bls_key(&private_key, "bls12381") - }, - } - } -} - -impl GenerateKey { - /// A test friendly typed key generation for x25519 keys. - pub async fn generate_x25519( - encoding: EncodingType, - key_file: &Path, - ) -> CliTypedResult<(x25519::PrivateKey, x25519::PublicKey)> { - let args = format!( - "generate --key-type {key_type:?} --output-file {key_file} --encoding {encoding:?} --assume-yes", - key_type = KeyType::X25519, - key_file = key_file.display(), - encoding = encoding, - ); - let command = GenerateKey::parse_from(args.split_whitespace()); - command.execute().await?; - Ok(( - encoding.load_key("private_key", key_file)?, - encoding.load_key( - "public_key", - &append_file_extension(key_file, PUBLIC_KEY_EXTENSION)?, - )?, - )) - } - - /// A test friendly typed key generation for e25519 keys. - pub async fn generate_ed25519( - encoding: EncodingType, - key_file: &Path, - ) -> CliTypedResult<(ed25519::Ed25519PrivateKey, ed25519::Ed25519PublicKey)> { - let args = format!( - "generate --key-type {key_type:?} --output-file {key_file} --encoding {encoding:?} --assume-yes", - key_type = KeyType::Ed25519, - key_file = key_file.display(), - encoding = encoding, - ); - let command = GenerateKey::parse_from(args.split_whitespace()); - command.execute().await?; - Ok(( - encoding.load_key("private_key", key_file)?, - encoding.load_key( - "public_key", - &append_file_extension(key_file, PUBLIC_KEY_EXTENSION)?, - )?, - )) - } -} - -#[derive(Debug, Parser)] -pub struct SaveKey { - #[clap(flatten)] - pub(crate) file_options: SaveFile, - #[clap(flatten)] - pub(crate) encoding_options: EncodingOptions, -} - -impl SaveKey { - /// Public key file name - fn public_key_file(&self) -> CliTypedResult { - append_file_extension( - self.file_options.output_file.as_path(), - PUBLIC_KEY_EXTENSION, - ) - } - - /// Public key file name - fn proof_of_possession_file(&self) -> CliTypedResult { - append_file_extension(self.file_options.output_file.as_path(), "pop") - } - - /// Check if the key file exists already - pub fn check_key_file(&self) -> CliTypedResult<()> { - // Check if file already exists - self.file_options.check_file()?; - check_if_file_exists(&self.public_key_file()?, self.file_options.prompt_options) - } - - /// Saves a key to a file encoded in a string - pub fn save_key( - self, - key: &Key, - key_name: &'static str, - ) -> CliTypedResult> { - let encoded_private_key = self.encoding_options.encoding.encode_key(key_name, key)?; - let encoded_public_key = self - .encoding_options - .encoding - .encode_key(key_name, &key.public_key())?; - - // Write private and public keys to files - let public_key_file = self.public_key_file()?; - self.file_options - .save_to_file_confidential(key_name, &encoded_private_key)?; - write_to_file(&public_key_file, key_name, &encoded_public_key)?; - - let mut map = HashMap::new(); - map.insert("PrivateKey Path", self.file_options.output_file); - map.insert("PublicKey Path", public_key_file); - Ok(map) - } - - /// Saves a key to a file encoded in a string - pub fn save_bls_key( - self, - key: &bls12381::PrivateKey, - key_name: &'static str, - ) -> CliTypedResult> { - let encoded_private_key = self.encoding_options.encoding.encode_key(key_name, key)?; - let encoded_public_key = self - .encoding_options - .encoding - .encode_key(key_name, &key.public_key())?; - let encoded_proof_of_posession = self - .encoding_options - .encoding - .encode_key(key_name, &bls12381::ProofOfPossession::create(key))?; - - // Write private and public keys to files - let public_key_file = self.public_key_file()?; - let proof_of_possession_file = self.proof_of_possession_file()?; - self.file_options - .save_to_file_confidential(key_name, &encoded_private_key)?; - write_to_file(&public_key_file, key_name, &encoded_public_key)?; - write_to_file( - &proof_of_possession_file, - key_name, - &encoded_proof_of_posession, - )?; - - let mut map = HashMap::new(); - map.insert("PrivateKey Path", self.file_options.output_file); - map.insert("PublicKey Path", public_key_file); - map.insert("Proof of possession Path", proof_of_possession_file); - Ok(map) - } -} diff --git a/m1/m1-cli/src/op/mod.rs b/m1/m1-cli/src/op/mod.rs deleted file mode 100644 index 989a2bb8e..000000000 --- a/m1/m1-cli/src/op/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod key; diff --git a/m1/m1-cli/src/stake/mod.rs b/m1/m1-cli/src/stake/mod.rs deleted file mode 100644 index 45168a706..000000000 --- a/m1/m1-cli/src/stake/mod.rs +++ /dev/null @@ -1,668 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - types::{ - CliCommand, CliError, CliResult, CliTypedResult, TransactionOptions, TransactionSummary, - }, - utils::prompt_yes_with_override, - }, - node::{get_stake_pools, StakePoolType}, -}; -use aptos_cached_packages::aptos_stdlib; -use aptos_types::{ - account_address::{ - create_vesting_contract_address, default_stake_pool_address, AccountAddress, - }, - vesting::VestingAdminStore, -}; -use async_trait::async_trait; -use clap::Parser; - -/// Tool for manipulating stake and stake pools -/// -#[derive(Parser)] -pub enum StakeTool { - AddStake(AddStake), - CreateStakingContract(CreateStakingContract), - DistributeVestedCoins(DistributeVestedCoins), - IncreaseLockup(IncreaseLockup), - InitializeStakeOwner(InitializeStakeOwner), - RequestCommission(RequestCommission), - SetDelegatedVoter(SetDelegatedVoter), - SetOperator(SetOperator), - UnlockStake(UnlockStake), - UnlockVestedCoins(UnlockVestedCoins), - WithdrawStake(WithdrawStake), -} - -impl StakeTool { - pub async fn execute(self) -> CliResult { - use StakeTool::*; - match self { - AddStake(tool) => tool.execute_serialized().await, - CreateStakingContract(tool) => tool.execute_serialized().await, - DistributeVestedCoins(tool) => tool.execute_serialized().await, - IncreaseLockup(tool) => tool.execute_serialized().await, - InitializeStakeOwner(tool) => tool.execute_serialized().await, - RequestCommission(tool) => tool.execute_serialized().await, - SetDelegatedVoter(tool) => tool.execute_serialized().await, - SetOperator(tool) => tool.execute_serialized().await, - UnlockStake(tool) => tool.execute_serialized().await, - UnlockVestedCoins(tool) => tool.execute_serialized().await, - WithdrawStake(tool) => tool.execute_serialized().await, - } - } -} - -/// Add APT to a stake pool -/// -/// This command allows stake pool owners to add APT to their stake. -#[derive(Parser)] -pub struct AddStake { - /// Amount of Octas (10^-8 APT) to add to stake - #[clap(long)] - pub amount: u64, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand> for AddStake { - fn command_name(&self) -> &'static str { - "AddStake" - } - - async fn execute(mut self) -> CliTypedResult> { - let client = self - .txn_options - .rest_options - .client(&self.txn_options.profile_options)?; - let amount = self.amount; - let owner_address = self.txn_options.sender_address()?; - let mut transaction_summaries: Vec = vec![]; - - let stake_pool_results = get_stake_pools(&client, owner_address).await?; - for stake_pool in stake_pool_results { - match stake_pool.pool_type { - StakePoolType::Direct => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::stake_add_stake(amount)) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::StakingContract => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::staking_contract_add_stake( - stake_pool.operator_address, - amount, - )) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::Vesting => { - return Err(CliError::UnexpectedError( - "Adding stake is not supported for vesting contracts".into(), - )) - }, - } - } - Ok(transaction_summaries) - } -} - -/// Unlock staked APT in a stake pool -/// -/// APT coins can only be unlocked if they no longer have an applied lockup period -#[derive(Parser)] -pub struct UnlockStake { - /// Amount of Octas (10^-8 APT) to unlock - #[clap(long)] - pub amount: u64, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand> for UnlockStake { - fn command_name(&self) -> &'static str { - "UnlockStake" - } - - async fn execute(mut self) -> CliTypedResult> { - let client = self - .txn_options - .rest_options - .client(&self.txn_options.profile_options)?; - let amount = self.amount; - let owner_address = self.txn_options.sender_address()?; - let mut transaction_summaries: Vec = vec![]; - - let stake_pool_results = get_stake_pools(&client, owner_address).await?; - for stake_pool in stake_pool_results { - match stake_pool.pool_type { - StakePoolType::Direct => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::stake_unlock(amount)) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::StakingContract => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::staking_contract_unlock_stake( - stake_pool.operator_address, - amount, - )) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::Vesting => { - return Err(CliError::UnexpectedError( - "Unlocking stake is not supported for vesting contracts".into(), - )) - }, - } - } - Ok(transaction_summaries) - } -} - -/// Withdraw unlocked staked APT from a stake pool -/// -/// This allows users to withdraw stake back into their CoinStore. -/// Before calling `WithdrawStake`, `UnlockStake` must be called first. -#[derive(Parser)] -pub struct WithdrawStake { - /// Amount of Octas (10^-8 APT) to withdraw. - /// This only applies to stake pools owned directly by the owner account, instead of via - /// a staking contract. In the latter case, when withdrawal is issued, all coins are distributed - #[clap(long)] - pub amount: u64, - - #[clap(flatten)] - pub(crate) node_op_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand> for WithdrawStake { - fn command_name(&self) -> &'static str { - "WithdrawStake" - } - - async fn execute(mut self) -> CliTypedResult> { - let client = self - .node_op_options - .rest_options - .client(&self.node_op_options.profile_options)?; - let amount = self.amount; - let owner_address = self.node_op_options.sender_address()?; - let mut transaction_summaries: Vec = vec![]; - - let stake_pool_results = get_stake_pools(&client, owner_address).await?; - for stake_pool in stake_pool_results { - match stake_pool.pool_type { - StakePoolType::Direct => { - transaction_summaries.push( - self.node_op_options - .submit_transaction(aptos_stdlib::stake_withdraw(amount)) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::StakingContract => { - transaction_summaries.push( - self.node_op_options - .submit_transaction(aptos_stdlib::staking_contract_distribute( - owner_address, - stake_pool.operator_address, - )) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::Vesting => { - return Err(CliError::UnexpectedError( - "Stake withdrawal from vesting contract should use distribute-vested-coins" - .into(), - )) - }, - } - } - Ok(transaction_summaries) - } -} - -/// Increase lockup of all staked APT in a stake pool -/// -/// Lockup may need to be increased in order to vote on a proposal. -#[derive(Parser)] -pub struct IncreaseLockup { - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand> for IncreaseLockup { - fn command_name(&self) -> &'static str { - "IncreaseLockup" - } - - async fn execute(mut self) -> CliTypedResult> { - let client = self - .txn_options - .rest_options - .client(&self.txn_options.profile_options)?; - let owner_address = self.txn_options.sender_address()?; - let mut transaction_summaries: Vec = vec![]; - - let stake_pool_results = get_stake_pools(&client, owner_address).await?; - for stake_pool in stake_pool_results { - match stake_pool.pool_type { - StakePoolType::Direct => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::stake_increase_lockup()) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::StakingContract => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::staking_contract_reset_lockup( - stake_pool.operator_address, - )) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::Vesting => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::vesting_reset_lockup( - stake_pool.vesting_contract.unwrap(), - )) - .await - .map(|inner| inner.into())?, - ); - }, - } - } - Ok(transaction_summaries) - } -} - -/// Initialize a stake pool owner -/// -/// Initializing stake owner adds the capability to delegate the -/// stake pool to an operator, or delegate voting to a different account. -#[derive(Parser)] -pub struct InitializeStakeOwner { - /// Initial amount of Octas (10^-8 APT) to be staked - #[clap(long)] - pub initial_stake_amount: u64, - - /// Account Address of delegated operator - #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] - pub operator_address: Option, - - /// Account address of delegated voter - #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] - pub voter_address: Option, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for InitializeStakeOwner { - fn command_name(&self) -> &'static str { - "InitializeStakeOwner" - } - - async fn execute(mut self) -> CliTypedResult { - let owner_address = self.txn_options.sender_address()?; - self.txn_options - .submit_transaction(aptos_stdlib::stake_initialize_stake_owner( - self.initial_stake_amount, - self.operator_address.unwrap_or(owner_address), - self.voter_address.unwrap_or(owner_address), - )) - .await - .map(|inner| inner.into()) - } -} - -/// Delegate operator capability to another account -/// -/// This changes teh operator capability from its current operator to a different operator. -/// By default, the operator of a stake pool is the owner of the stake pool -#[derive(Parser)] -pub struct SetOperator { - /// Account Address of delegated operator - /// - /// If not specified, it will be the same as the owner - #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] - pub operator_address: AccountAddress, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand> for SetOperator { - fn command_name(&self) -> &'static str { - "SetOperator" - } - - async fn execute(mut self) -> CliTypedResult> { - let client = self - .txn_options - .rest_options - .client(&self.txn_options.profile_options)?; - let owner_address = self.txn_options.sender_address()?; - let new_operator_address = self.operator_address; - let mut transaction_summaries: Vec = vec![]; - - let stake_pool_results = get_stake_pools(&client, owner_address).await?; - for stake_pool in stake_pool_results { - match stake_pool.pool_type { - StakePoolType::Direct => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::stake_set_operator( - new_operator_address, - )) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::StakingContract => { - transaction_summaries.push( - self.txn_options - .submit_transaction( - aptos_stdlib::staking_contract_switch_operator_with_same_commission( - stake_pool.operator_address, - new_operator_address, - ), - ) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::Vesting => { - transaction_summaries.push( - self.txn_options - .submit_transaction( - aptos_stdlib::vesting_update_operator_with_same_commission( - stake_pool.vesting_contract.unwrap(), - new_operator_address, - ), - ) - .await - .map(|inner| inner.into())?, - ); - }, - } - } - Ok(transaction_summaries) - } -} - -/// Delegate voting capability to another account -/// -/// Delegates voting capability from its current voter to a different voter. -/// By default, the voter of a stake pool is the owner of the stake pool -#[derive(Parser)] -pub struct SetDelegatedVoter { - /// Account Address of delegated voter - /// - /// If not specified, it will be the same as the owner - #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] - pub voter_address: AccountAddress, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand> for SetDelegatedVoter { - fn command_name(&self) -> &'static str { - "SetDelegatedVoter" - } - - async fn execute(mut self) -> CliTypedResult> { - let client = self - .txn_options - .rest_options - .client(&self.txn_options.profile_options)?; - let owner_address = self.txn_options.sender_address()?; - let new_voter_address = self.voter_address; - let mut transaction_summaries: Vec = vec![]; - - let stake_pool_results = get_stake_pools(&client, owner_address).await?; - for stake_pool in stake_pool_results { - match stake_pool.pool_type { - StakePoolType::Direct => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::stake_set_delegated_voter( - new_voter_address, - )) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::StakingContract => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::staking_contract_update_voter( - stake_pool.operator_address, - new_voter_address, - )) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::Vesting => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::vesting_update_voter( - stake_pool.vesting_contract.unwrap(), - new_voter_address, - )) - .await - .map(|inner| inner.into())?, - ); - }, - } - } - Ok(transaction_summaries) - } -} - -/// Create a staking contract stake pool -/// -/// -#[derive(Parser)] -pub struct CreateStakingContract { - /// Account Address of operator - #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] - pub operator: AccountAddress, - - /// Account Address of delegated voter - #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] - pub voter: AccountAddress, - - /// Amount to create the staking contract with - #[clap(long)] - pub amount: u64, - - /// Percentage of accumulated rewards to pay the operator as commission - #[clap(long)] - pub commission_percentage: u64, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for CreateStakingContract { - fn command_name(&self) -> &'static str { - "CreateStakingContract" - } - - async fn execute(mut self) -> CliTypedResult { - let pool_address = default_stake_pool_address( - self.txn_options.profile_options.account_address()?, - self.operator, - ); - prompt_yes_with_override( - &format!( - "Creating a new staking contract with pool address 0x{}. Confirm?", - pool_address - ), - self.txn_options.prompt_options, - )?; - - self.txn_options - .submit_transaction(aptos_stdlib::staking_contract_create_staking_contract( - self.operator, - self.voter, - self.amount, - self.commission_percentage, - vec![], - )) - .await - .map(|inner| inner.into()) - } -} - -/// Distribute fully unlocked coins from vesting -/// -/// Distribute fully unlocked coins (rewards and/or vested coins) from the vesting contract -/// to shareholders. -#[derive(Parser)] -pub struct DistributeVestedCoins { - /// Address of the vesting contract's admin. - #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] - pub admin_address: AccountAddress, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for DistributeVestedCoins { - fn command_name(&self) -> &'static str { - "DistributeVestedCoins" - } - - async fn execute(mut self) -> CliTypedResult { - let vesting_contract_address = create_vesting_contract_address(self.admin_address, 0, &[]); - self.txn_options - .submit_transaction(aptos_stdlib::vesting_distribute(vesting_contract_address)) - .await - .map(|inner| inner.into()) - } -} - -/// Unlock vested coins -/// -/// Unlock vested coins according to the vesting contract's schedule. -/// This also unlocks any accumulated staking rewards and pays commission to the operator of the -/// vesting contract's stake pool first. -/// -/// The unlocked vested tokens and staking rewards are still subject to the staking lockup and -/// cannot be withdrawn until after the lockup expires. -#[derive(Parser)] -pub struct UnlockVestedCoins { - /// Address of the vesting contract's admin. - #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] - pub admin_address: AccountAddress, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for UnlockVestedCoins { - fn command_name(&self) -> &'static str { - "UnlockVestedCoins" - } - - async fn execute(mut self) -> CliTypedResult { - let vesting_contract_address = create_vesting_contract_address(self.admin_address, 0, &[]); - self.txn_options - .submit_transaction(aptos_stdlib::vesting_vest(vesting_contract_address)) - .await - .map(|inner| inner.into()) - } -} - -/// Request commission from running a stake pool -/// -/// Allows operators or owners to request commission from running a stake pool (only if there's a -/// staking contract set up with the staker). The commission will be withdrawable at the end of the -/// stake pool's current lockup period. -#[derive(Parser)] -pub struct RequestCommission { - /// Address of the owner of the stake pool - #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] - pub owner_address: AccountAddress, - - /// Address of the operator of the stake pool - #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] - pub operator_address: AccountAddress, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for RequestCommission { - fn command_name(&self) -> &'static str { - "RequestCommission" - } - - async fn execute(mut self) -> CliTypedResult { - let client = self - .txn_options - .rest_options - .client(&self.txn_options.profile_options)?; - - // If this is a vesting stake pool, retrieve the associated vesting contract - let vesting_admin_store = client - .get_account_resource_bcs::( - self.owner_address, - "0x1::vesting::AdminStore", - ) - .await; - - // Note: this only works if the vesting contract has exactly one staking contract - // associated - let staker_address = if let Ok(vesting_admin_store) = vesting_admin_store { - vesting_admin_store.into_inner().vesting_contracts[0] - } else { - self.owner_address - }; - self.txn_options - .submit_transaction(aptos_stdlib::staking_contract_request_commission( - staker_address, - self.operator_address, - )) - .await - .map(|inner| inner.into()) - } -} diff --git a/m1/m1-cli/src/test/mod.rs b/m1/m1-cli/src/test/mod.rs deleted file mode 100644 index aec14d4a7..000000000 --- a/m1/m1-cli/src/test/mod.rs +++ /dev/null @@ -1,1221 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - account::{ - create::{CreateAccount, DEFAULT_FUNDED_COINS}, - fund::FundWithFaucet, - key_rotation::{LookupAddress, RotateKey, RotateSummary}, - list::{ListAccount, ListQuery}, - transfer::{TransferCoins, TransferSummary}, - }, - common::{ - init::{InitTool, Network}, - types::{ - account_address_from_public_key, AccountAddressWrapper, ArgWithTypeVec, CliError, - CliTypedResult, EncodingOptions, EntryFunctionArguments, FaucetOptions, GasOptions, - KeyType, MoveManifestAccountWrapper, MovePackageDir, OptionalPoolAddressArgs, - PoolAddressArgs, PrivateKeyInputOptions, PromptOptions, PublicKeyInputOptions, - RestOptions, RngArgs, SaveFile, TransactionOptions, TransactionSummary, - }, - utils::write_to_file, - }, - governance::{ - CompileScriptFunction, ProposalSubmissionSummary, SubmitProposal, SubmitVote, - VerifyProposal, VerifyProposalResponse, - }, - move_tool::{ - ArgWithType, CompilePackage, DownloadPackage, FrameworkPackageArgs, IncludedArtifacts, - IncludedArtifactsArgs, InitPackage, MemberId, PublishPackage, RunFunction, RunScript, - TestPackage, - }, - node::{ - AnalyzeMode, AnalyzeValidatorPerformance, GetStakePool, InitializeValidator, - JoinValidatorSet, LeaveValidatorSet, OperatorArgs, OperatorConfigFileArgs, - ShowValidatorConfig, ShowValidatorSet, ShowValidatorStake, StakePoolResult, - UpdateConsensusKey, UpdateValidatorNetworkAddresses, ValidatorConfig, - ValidatorConsensusKeyArgs, ValidatorNetworkAddressesArgs, - }, - op::key::{ExtractPeer, GenerateKey, NetworkKeyInputOptions, SaveKey}, - stake::{ - AddStake, IncreaseLockup, InitializeStakeOwner, SetDelegatedVoter, SetOperator, - UnlockStake, WithdrawStake, - }, - CliCommand, -}; -use aptos_config::config::Peer; -use aptos_crypto::{ - bls12381, - ed25519::{Ed25519PrivateKey, Ed25519PublicKey}, - x25519, PrivateKey, -}; -use aptos_genesis::config::HostAndPort; -use aptos_keygen::KeyGen; -use aptos_logger::warn; -use aptos_rest_client::{ - aptos_api_types::{MoveStructTag, MoveType}, - Transaction, -}; -use aptos_sdk::move_types::{account_address::AccountAddress, language_storage::ModuleId}; -use aptos_temppath::TempPath; -use aptos_types::on_chain_config::ValidatorSet; -use move_core_types::ident_str; -use reqwest::Url; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -use std::{ - collections::{BTreeMap, HashMap}, - mem, - path::PathBuf, - str::FromStr, - time::Duration, -}; -use tempfile::TempDir; -use thiserror::__private::AsDisplay; -#[cfg(feature = "cli-framework-test-move")] -use thiserror::__private::AsDisplay; -use tokio::time::{sleep, Instant}; - -#[cfg(test)] -mod tests; - -pub const INVALID_ACCOUNT: &str = "0xDEADBEEFCAFEBABE"; - -pub const FIRST_MOVE_FILE: &str = " -module NamedAddress0::store { - use std::string; - use aptos_framework::coin::{Self}; - - struct CoolCoin has key {} - - public entry fun init( - account: &signer, - decimals: u64, - monitor_supply: bool - ) { - let (_, _) = coin::initialize(account, string::utf8(b\"CoolCoin\"), string::utf8(b\"COOL\"), decimals, monitor_supply); - coin::register(account); - } -}"; - -/// A framework for testing the CLI -pub struct CliTestFramework { - account_addresses: Vec, - account_keys: Vec, - endpoint: Url, - faucet_endpoint: Url, - move_dir: Option, -} - -impl CliTestFramework { - pub fn local_new(num_accounts: usize) -> CliTestFramework { - let dummy_url = Url::parse("http://localhost").unwrap(); - let mut framework = CliTestFramework { - account_addresses: Vec::new(), - account_keys: Vec::new(), - endpoint: dummy_url.clone(), - faucet_endpoint: dummy_url, - move_dir: None, - }; - let mut keygen = KeyGen::from_seed([0; 32]); - for _ in 0..num_accounts { - let key = keygen.generate_ed25519_private_key(); - framework.add_account_to_cli(key); - } - framework - } - - pub async fn new(endpoint: Url, faucet_endpoint: Url, num_accounts: usize) -> CliTestFramework { - let mut framework = CliTestFramework { - account_addresses: Vec::new(), - account_keys: Vec::new(), - endpoint, - faucet_endpoint, - move_dir: None, - }; - let mut keygen = KeyGen::from_seed([0; 32]); - - for _ in 0..num_accounts { - framework - .create_cli_account_from_faucet(keygen.generate_ed25519_private_key(), None) - .await - .unwrap(); - } - - framework - } - - pub fn addresses(&self) -> Vec { - self.account_addresses.clone() - } - - async fn check_account_exists(&self, index: usize) -> bool { - // Create account if it doesn't exist (and there's a faucet) - let client = aptos_rest_client::Client::new(self.endpoint.clone()); - let address = self.account_id(index); - client.get_account(address).await.is_ok() - } - - pub fn add_account_to_cli(&mut self, private_key: Ed25519PrivateKey) -> usize { - let address = account_address_from_public_key(&private_key.public_key()); - self.account_addresses.push(address); - self.account_keys.push(private_key); - println!( - "Account: {} (index: {})", - address.to_hex_literal(), - self.account_keys.len() - 1 - ); - self.account_keys.len() - 1 - } - - pub fn add_account_with_address_to_cli( - &mut self, - private_key: Ed25519PrivateKey, - address: AccountAddress, - ) -> usize { - self.account_addresses.push(address); - self.account_keys.push(private_key); - self.account_keys.len() - 1 - } - - pub async fn create_cli_account( - &mut self, - private_key: Ed25519PrivateKey, - sender_index: usize, - ) -> CliTypedResult { - let index = self.add_account_to_cli(private_key); - if self.check_account_exists(index).await { - return Err(CliError::UnexpectedError( - "Account already exists".to_string(), - )); - } - CreateAccount { - txn_options: self.transaction_options(sender_index, None), - account: self.account_id(index), - } - .execute() - .await?; - - Ok(index) - } - - pub async fn create_cli_account_from_faucet( - &mut self, - private_key: Ed25519PrivateKey, - amount: Option, - ) -> CliTypedResult { - let index = self.add_account_to_cli(private_key); - if self.check_account_exists(index).await { - return Err(CliError::UnexpectedError( - "Account already exists".to_string(), - )); - } - - self.fund_account(index, amount).await?; - warn!( - "Funded account {:?} with {:?} OCTA", - self.account_id(index), - amount.unwrap_or(DEFAULT_FUNDED_COINS) - ); - Ok(index) - } - - pub async fn fund_account(&self, index: usize, amount: Option) -> CliTypedResult { - FundWithFaucet { - profile_options: Default::default(), - account: self.account_id(index), - faucet_options: self.faucet_options(), - amount: amount.unwrap_or(DEFAULT_FUNDED_COINS), - rest_options: self.rest_options(), - } - .execute() - .await - } - - pub async fn lookup_address( - &self, - public_key: &Ed25519PublicKey, - ) -> CliTypedResult { - LookupAddress { - public_key_options: PublicKeyInputOptions::from_key(public_key), - rest_options: self.rest_options(), - encoding_options: Default::default(), - profile_options: Default::default(), - } - .execute() - .await - } - - pub async fn rotate_key( - &mut self, - index: usize, - new_private_key: String, - gas_options: Option, - ) -> CliTypedResult { - let response = RotateKey { - txn_options: TransactionOptions { - private_key_options: PrivateKeyInputOptions::from_private_key( - self.private_key(index), - ) - .unwrap(), - sender_account: Some(self.account_id(index)), - rest_options: self.rest_options(), - gas_options: gas_options.unwrap_or_default(), - prompt_options: PromptOptions::yes(), - ..Default::default() - }, - new_private_key: Some(new_private_key), - save_to_profile: None, - new_private_key_file: None, - skip_saving_profile: true, - } - .execute() - .await?; - - Ok(response) - } - - pub async fn list_account(&self, index: usize, query: ListQuery) -> CliTypedResult> { - ListAccount { - rest_options: self.rest_options(), - profile_options: Default::default(), - account: Some(self.account_id(index)), - query, - } - .execute() - .await - } - - pub async fn transfer_coins( - &self, - sender_index: usize, - receiver_index: usize, - amount: u64, - gas_options: Option, - ) -> CliTypedResult { - TransferCoins { - txn_options: self.transaction_options(sender_index, gas_options), - account: self.account_id(receiver_index), - amount, - } - .execute() - .await - } - - pub async fn transfer_invalid_addr( - &self, - sender_index: usize, - amount: u64, - gas_options: Option, - ) -> CliTypedResult { - RunFunction { - entry_function_args: EntryFunctionArguments { - function_id: MemberId { - module_id: ModuleId::new(AccountAddress::ONE, ident_str!("coin").into()), - member_id: ident_str!("transfer").into(), - }, - arg_vec: ArgWithTypeVec { - args: vec![ - ArgWithType::from_str("address:0xdeadbeefcafebabe").unwrap(), - ArgWithType::from_str(&format!("u64:{}", amount)).unwrap(), - ], - }, - type_args: vec![MoveType::Struct(MoveStructTag::new( - AccountAddress::ONE.into(), - ident_str!("aptos_coin").into(), - ident_str!("AptosCoin").into(), - vec![], - ))], - }, - txn_options: self.transaction_options(sender_index, gas_options), - } - .execute() - .await - } - - pub async fn show_validator_config( - &self, - pool_index: usize, - ) -> CliTypedResult { - ShowValidatorConfig { - rest_options: self.rest_options(), - profile_options: Default::default(), - operator_args: self.operator_args(Some(pool_index)), - } - .execute() - .await - .map(|v| (&v).into()) - } - - pub async fn show_validator_set(&self) -> CliTypedResult { - ShowValidatorSet { - rest_options: self.rest_options(), - profile_options: Default::default(), - } - .execute() - .await - .map(|v| (&v).into()) - } - - pub async fn show_validator_stake(&self, pool_index: usize) -> CliTypedResult { - ShowValidatorStake { - rest_options: self.rest_options(), - profile_options: Default::default(), - operator_args: self.operator_args(Some(pool_index)), - } - .execute() - .await - } - - pub async fn initialize_validator( - &self, - index: usize, - consensus_public_key: bls12381::PublicKey, - proof_of_possession: bls12381::ProofOfPossession, - validator_host: HostAndPort, - validator_network_public_key: x25519::PublicKey, - ) -> CliTypedResult { - InitializeValidator { - txn_options: self.transaction_options(index, None), - operator_config_file_args: OperatorConfigFileArgs { - operator_config_file: None, - }, - validator_consensus_key_args: ValidatorConsensusKeyArgs { - consensus_public_key: Some(consensus_public_key), - proof_of_possession: Some(proof_of_possession), - }, - validator_network_addresses_args: ValidatorNetworkAddressesArgs { - validator_host: Some(validator_host), - validator_network_public_key: Some(validator_network_public_key), - full_node_host: None, - full_node_network_public_key: None, - }, - } - .execute() - .await - } - - pub async fn add_stake( - &self, - index: usize, - amount: u64, - ) -> CliTypedResult> { - AddStake { - txn_options: self.transaction_options(index, None), - amount, - } - .execute() - .await - } - - pub async fn unlock_stake( - &self, - index: usize, - amount: u64, - ) -> CliTypedResult> { - UnlockStake { - txn_options: self.transaction_options(index, None), - amount, - } - .execute() - .await - } - - pub async fn withdraw_stake( - &self, - index: usize, - amount: u64, - ) -> CliTypedResult> { - WithdrawStake { - node_op_options: self.transaction_options(index, None), - amount, - } - .execute() - .await - } - - pub async fn increase_lockup(&self, index: usize) -> CliTypedResult> { - IncreaseLockup { - txn_options: self.transaction_options(index, None), - } - .execute() - .await - } - - pub async fn join_validator_set( - &self, - operator_index: usize, - pool_index: Option, - ) -> CliTypedResult { - JoinValidatorSet { - txn_options: self.transaction_options(operator_index, None), - operator_args: self.operator_args(pool_index), - } - .execute() - .await - } - - pub async fn leave_validator_set( - &self, - operator_index: usize, - pool_index: Option, - ) -> CliTypedResult { - LeaveValidatorSet { - txn_options: self.transaction_options(operator_index, None), - operator_args: self.operator_args(pool_index), - } - .execute() - .await - } - - pub async fn update_validator_network_addresses( - &self, - operator_index: usize, - pool_index: Option, - validator_host: HostAndPort, - validator_network_public_key: x25519::PublicKey, - ) -> CliTypedResult { - UpdateValidatorNetworkAddresses { - txn_options: self.transaction_options(operator_index, None), - operator_args: self.operator_args(pool_index), - operator_config_file_args: OperatorConfigFileArgs { - operator_config_file: None, - }, - validator_network_addresses_args: ValidatorNetworkAddressesArgs { - validator_host: Some(validator_host), - validator_network_public_key: Some(validator_network_public_key), - full_node_host: None, - full_node_network_public_key: None, - }, - } - .execute() - .await - } - - pub async fn analyze_validator_performance( - &self, - start_epoch: Option, - end_epoch: Option, - ) -> CliTypedResult<()> { - AnalyzeValidatorPerformance { - start_epoch: start_epoch.unwrap_or(-2), - end_epoch, - rest_options: self.rest_options(), - profile_options: Default::default(), - analyze_mode: AnalyzeMode::All, - pool_addresses: vec![], - } - .execute() - .await - } - - pub async fn update_consensus_key( - &self, - operator_index: usize, - pool_index: Option, - consensus_public_key: bls12381::PublicKey, - proof_of_possession: bls12381::ProofOfPossession, - ) -> CliTypedResult { - UpdateConsensusKey { - txn_options: self.transaction_options(operator_index, None), - operator_args: self.operator_args(pool_index), - operator_config_file_args: OperatorConfigFileArgs { - operator_config_file: None, - }, - validator_consensus_key_args: ValidatorConsensusKeyArgs { - consensus_public_key: Some(consensus_public_key), - proof_of_possession: Some(proof_of_possession), - }, - } - .execute() - .await - } - - pub async fn init(&self, private_key: &Ed25519PrivateKey) -> CliTypedResult<()> { - InitTool { - network: Some(Network::Custom), - rest_url: Some(self.endpoint.clone()), - faucet_url: Some(self.faucet_endpoint.clone()), - rng_args: RngArgs::from_seed([0; 32]), - private_key_options: PrivateKeyInputOptions::from_private_key(private_key)?, - profile_options: Default::default(), - prompt_options: PromptOptions::yes(), - encoding_options: EncodingOptions::default(), - skip_faucet: false, - } - .execute() - .await - } - - pub async fn get_pool_address( - &self, - owner_index: usize, - ) -> CliTypedResult> { - GetStakePool { - owner_address: self.account_id(owner_index), - rest_options: self.rest_options(), - profile_options: Default::default(), - } - .execute() - .await - } - - pub async fn initialize_stake_owner( - &self, - owner_index: usize, - initial_stake_amount: u64, - voter_index: Option, - operator_index: Option, - ) -> CliTypedResult { - InitializeStakeOwner { - txn_options: self.transaction_options(owner_index, None), - initial_stake_amount, - operator_address: operator_index.map(|idx| self.account_id(idx)), - voter_address: voter_index.map(|idx| self.account_id(idx)), - } - .execute() - .await - } - - pub async fn create_stake_pool( - &self, - owner_index: usize, - operator_index: usize, - voter_index: usize, - amount: u64, - commission_percentage: u64, - ) -> CliTypedResult { - RunFunction { - entry_function_args: EntryFunctionArguments { - function_id: MemberId::from_str("0x1::staking_contract::create_staking_contract") - .unwrap(), - arg_vec: ArgWithTypeVec { - args: vec![ - ArgWithType::address(self.account_id(operator_index)), - ArgWithType::address(self.account_id(voter_index)), - ArgWithType::u64(amount), - ArgWithType::u64(commission_percentage), - ArgWithType::bytes(vec![]), - ], - }, - type_args: vec![], - }, - txn_options: self.transaction_options(owner_index, None), - } - .execute() - .await - } - - pub async fn set_operator( - &self, - owner_index: usize, - operator_index: usize, - ) -> CliTypedResult> { - SetOperator { - txn_options: self.transaction_options(owner_index, None), - operator_address: self.account_id(operator_index), - } - .execute() - .await - } - - pub async fn set_delegated_voter( - &self, - owner_index: usize, - voter_index: usize, - ) -> CliTypedResult> { - SetDelegatedVoter { - txn_options: self.transaction_options(owner_index, None), - voter_address: self.account_id(voter_index), - } - .execute() - .await - } - - /// Wait for an account to exist - pub async fn wait_for_account(&self, index: usize) -> CliTypedResult> { - let mut result = self.list_account(index, ListQuery::Balance).await; - let start = Instant::now(); - while start.elapsed() < Duration::from_secs(10) { - match result { - Ok(_) => return result, - _ => { - sleep(Duration::from_millis(500)).await; - result = self.list_account(index, ListQuery::Balance).await; - }, - }; - } - - result - } - - pub async fn account_balance_now(&self, index: usize) -> CliTypedResult { - let result = self.list_account(index, ListQuery::Balance).await?; - Ok(json_account_to_balance(result.first().unwrap())) - } - - pub async fn assert_account_balance_now(&self, index: usize, expected: u64) { - let result = self.list_account(index, ListQuery::Balance).await; - assert!( - result.is_ok(), - "Account {} not yet created, {}, last 10 transactions: {}", - self.account_id(index), - result.unwrap_err(), - self.last_n_transactions_details(10).await - ); - let accounts = result.unwrap(); - let account = accounts.first().unwrap(); - let coin = json_account_to_balance(account); - assert_eq!( - coin, - expected, - "Account {} with state: {:?}, last 10 transactions: {}", - self.account_id(index), - account, - self.last_n_transactions_details(10).await - ); - } - - async fn last_n_transactions_details(&self, count: u16) -> String { - let result = aptos_rest_client::Client::new(self.endpoint.clone()) - .get_transactions(None, Some(count)) - .await; - if let Err(e) = result { - return format!("Err({:?})", e); - } - let lines = result - .unwrap() - .inner() - .iter() - .map(|t| { - if let Transaction::UserTransaction(u) = t { - format!( - " * [{}] {}: sender={}, payload={:?}", - t.version().unwrap_or(0), - t.vm_status(), - u.request.sender, - u.request.payload - ) - } else { - format!( - " * [{}] {}: {}", - t.version().unwrap_or(0), - t.vm_status(), - t.type_str() - ) - } - }) - .collect::>(); - format!("\n{}\n", lines.join("\n")) - } - - pub async fn generate_x25519_key( - &self, - output_file: PathBuf, - seed: [u8; 32], - ) -> CliTypedResult> { - GenerateKey { - key_type: KeyType::X25519, - rng_args: RngArgs::from_seed(seed), - save_params: SaveKey { - file_options: SaveFile { - output_file, - prompt_options: PromptOptions::yes(), - }, - encoding_options: Default::default(), - }, - vanity_prefix: None, - vanity_multisig: false, - } - .execute() - .await - } - - pub async fn extract_peer( - &self, - host: HostAndPort, - private_key_file: PathBuf, - output_file: PathBuf, - ) -> CliTypedResult> { - ExtractPeer { - host, - network_key_input_options: NetworkKeyInputOptions::from_private_key_file( - private_key_file, - ), - output_file_options: SaveFile { - output_file, - prompt_options: PromptOptions::yes(), - }, - encoding_options: Default::default(), - } - .execute() - .await - } - - pub fn init_move_dir(&mut self) { - let move_dir = TempPath::new(); - move_dir - .create_as_dir() - .expect("Expected to be able to create move temp dir"); - self.move_dir = Some(move_dir.path().to_path_buf()); - } - - #[cfg(feature = "cli-framework-test-move")] - pub fn add_move_files(&self) { - let move_dir = self.move_dir(); - let sources_dir = move_dir.join("sources"); - - let hello_blockchain_contents = include_str!( - "../../../../aptos-move/move-examples/hello_blockchain/sources/hello_blockchain.move" - ); - let source_path = sources_dir.join("hello_blockchain.move"); - write_to_file( - source_path.as_path(), - &source_path.as_display().to_string(), - hello_blockchain_contents.as_bytes(), - ) - .unwrap(); - - let hello_blockchain_test_contents = include_str!("../../../../aptos-move/move-examples/hello_blockchain/sources/hello_blockchain_test.move"); - let test_path = sources_dir.join("hello_blockchain_test.move"); - write_to_file( - test_path.as_path(), - &test_path.as_display().to_string(), - hello_blockchain_test_contents.as_bytes(), - ) - .unwrap(); - } - - pub fn move_dir(&self) -> PathBuf { - assert!(self.move_dir.is_some(), "Must have initialized the temp move directory with `CliTestFramework::init_move_dir()` first"); - self.move_dir.as_ref().cloned().unwrap() - } - - pub async fn init_package( - &self, - name: String, - account_strs: BTreeMap<&str, &str>, - framework_dir: Option, - ) -> CliTypedResult<()> { - InitPackage { - name, - package_dir: Some(self.move_dir()), - named_addresses: Self::move_manifest_named_addresses(account_strs), - prompt_options: PromptOptions { - assume_yes: false, - assume_no: true, - }, - framework_package_args: FrameworkPackageArgs { - framework_git_rev: None, - framework_local_dir: framework_dir, - skip_fetch_latest_git_deps: false, - }, - } - .execute() - .await - } - - pub async fn compile_package( - &self, - account_strs: BTreeMap<&str, &str>, - included_artifacts: Option, - ) -> CliTypedResult> { - CompilePackage { - move_options: self.move_options(account_strs), - save_metadata: false, - included_artifacts_args: IncludedArtifactsArgs { - included_artifacts: included_artifacts.unwrap_or(IncludedArtifacts::Sparse), - }, - } - .execute() - .await - } - - pub async fn test_package( - &self, - account_strs: BTreeMap<&str, &str>, - filter: Option<&str>, - ) -> CliTypedResult<&'static str> { - TestPackage { - instruction_execution_bound: 100_000, - move_options: self.move_options(account_strs), - filter: filter.map(|str| str.to_string()), - ignore_compile_warnings: false, - compute_coverage: false, - dump_state: false, - } - .execute() - .await - } - - pub async fn publish_package( - &self, - index: usize, - gas_options: Option, - account_strs: BTreeMap<&str, &str>, - included_artifacts: Option, - ) -> CliTypedResult { - PublishPackage { - move_options: self.move_options(account_strs), - txn_options: self.transaction_options(index, gas_options), - override_size_check: false, - included_artifacts_args: IncludedArtifactsArgs { - included_artifacts: included_artifacts.unwrap_or(IncludedArtifacts::Sparse), - }, - } - .execute() - .await - } - - pub async fn download_package( - &self, - index: usize, - package: String, - output_dir: PathBuf, - ) -> CliTypedResult<&'static str> { - DownloadPackage { - rest_options: self.rest_options(), - profile_options: Default::default(), - account: self.account_id(index), - package, - output_dir: Some(output_dir), - } - .execute() - .await - } - - pub async fn run_function( - &self, - index: usize, - gas_options: Option, - function_id: MemberId, - args: Vec<&str>, - type_args: Vec<&str>, - ) -> CliTypedResult { - let mut parsed_args = Vec::new(); - for arg in args { - parsed_args.push( - ArgWithType::from_str(arg) - .map_err(|err| CliError::UnexpectedError(err.to_string()))?, - ) - } - - let mut parsed_type_args = Vec::new(); - for arg in type_args { - parsed_type_args.push( - MoveType::from_str(arg) - .map_err(|err| CliError::UnexpectedError(err.to_string()))?, - ) - } - - RunFunction { - txn_options: self.transaction_options(index, gas_options), - entry_function_args: EntryFunctionArguments { - function_id, - arg_vec: ArgWithTypeVec { args: parsed_args }, - type_args: parsed_type_args, - }, - } - .execute() - .await - } - - /// Runs the given script contents using the local aptos_framework directory. - pub async fn run_script( - &self, - index: usize, - script_contents: &str, - ) -> CliTypedResult { - self.run_script_with_framework_package(index, script_contents, FrameworkPackageArgs { - framework_git_rev: None, - framework_local_dir: Some(Self::aptos_framework_dir()), - skip_fetch_latest_git_deps: false, - }) - .await - } - - /// Runs the given script contents using the aptos_framework from aptos-core git repository. - pub async fn run_script_with_default_framework( - &self, - index: usize, - script_contents: &str, - ) -> CliTypedResult { - self.run_script_with_framework_package(index, script_contents, FrameworkPackageArgs { - framework_git_rev: None, - framework_local_dir: None, - skip_fetch_latest_git_deps: false, - }) - .await - } - - /// Runs the given script with the provided framework package arguments - pub async fn run_script_with_framework_package( - &self, - index: usize, - script_contents: &str, - framework_package_args: FrameworkPackageArgs, - ) -> CliTypedResult { - // Make a temporary directory for compilation - let temp_dir = TempDir::new().map_err(|err| { - CliError::UnexpectedError(format!("Failed to create temporary directory {}", err)) - })?; - - let source_path = temp_dir.path().join("script.move"); - write_to_file( - source_path.as_path(), - &source_path.as_display().to_string(), - script_contents.as_bytes(), - ) - .unwrap(); - - RunScript { - txn_options: self.transaction_options(index, None), - compile_proposal_args: CompileScriptFunction { - script_path: Some(source_path), - compiled_script_path: None, - framework_package_args, - bytecode_version: None, - }, - arg_vec: ArgWithTypeVec { args: Vec::new() }, - type_args: Vec::new(), - } - .execute() - .await - } - - pub async fn run_script_with_script_path( - &self, - index: usize, - script_path: &str, - args: Vec, - type_args: Vec, - ) -> CliTypedResult { - RunScript { - txn_options: self.transaction_options(index, None), - compile_proposal_args: CompileScriptFunction { - script_path: Some(script_path.parse().unwrap()), - compiled_script_path: None, - framework_package_args: FrameworkPackageArgs { - framework_git_rev: None, - framework_local_dir: Some(Self::aptos_framework_dir()), - skip_fetch_latest_git_deps: false, - }, - bytecode_version: None, - }, - arg_vec: ArgWithTypeVec { args }, - type_args, - } - .execute() - .await - } - - fn aptos_framework_dir() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("..") - .join("..") - .join("aptos-move") - .join("framework") - .join("aptos-framework") - } - - pub fn move_options(&self, account_strs: BTreeMap<&str, &str>) -> MovePackageDir { - MovePackageDir { - package_dir: Some(self.move_dir()), - output_dir: None, - named_addresses: Self::named_addresses(account_strs), - skip_fetch_latest_git_deps: true, - bytecode_version: None, - } - } - - pub fn move_manifest_named_addresses( - account_strs: BTreeMap<&str, &str>, - ) -> BTreeMap { - account_strs - .iter() - .map(|(key, value)| { - ( - key.to_string(), - MoveManifestAccountWrapper::from_str(value).unwrap(), - ) - }) - .collect() - } - - pub fn named_addresses( - account_strs: BTreeMap<&str, &str>, - ) -> BTreeMap { - account_strs - .iter() - .map(|(key, value)| { - ( - key.to_string(), - AccountAddressWrapper::from_str(value).unwrap(), - ) - }) - .collect() - } - - pub fn rest_options(&self) -> RestOptions { - RestOptions::new(Some(self.endpoint.clone()), None) - } - - pub fn faucet_options(&self) -> FaucetOptions { - FaucetOptions::new(Some(self.faucet_endpoint.clone())) - } - - fn transaction_options( - &self, - index: usize, - gas_options: Option, - ) -> TransactionOptions { - TransactionOptions { - private_key_options: PrivateKeyInputOptions::from_private_key(self.private_key(index)) - .unwrap(), - sender_account: Some(self.account_id(index)), - rest_options: self.rest_options(), - gas_options: gas_options.unwrap_or_default(), - prompt_options: PromptOptions::yes(), - ..Default::default() - } - } - - fn operator_args(&self, pool_index: Option) -> OperatorArgs { - OperatorArgs { - pool_address_args: OptionalPoolAddressArgs { - pool_address: pool_index.map(|idx| self.account_id(idx)), - }, - } - } - - pub fn private_key(&self, index: usize) -> &Ed25519PrivateKey { - self.account_keys.get(index).unwrap() - } - - pub fn set_private_key( - &mut self, - index: usize, - new_key: Ed25519PrivateKey, - ) -> Ed25519PrivateKey { - // Insert the new private key into the test framework, returning the old one - mem::replace(&mut self.account_keys[index], new_key) - } - - pub fn account_id(&self, index: usize) -> AccountAddress { - *self.account_addresses.get(index).unwrap() - } - - pub async fn create_proposal( - &mut self, - index: usize, - metadata_url: &str, - script_path: PathBuf, - pool_address: AccountAddress, - is_multi_step: bool, - ) -> CliTypedResult { - SubmitProposal { - metadata_url: Url::parse(metadata_url).unwrap(), - pool_address_args: PoolAddressArgs { pool_address }, - txn_options: self.transaction_options(index, None), - is_multi_step, - compile_proposal_args: CompileScriptFunction { - script_path: Some(script_path), - compiled_script_path: None, - framework_package_args: FrameworkPackageArgs { - framework_git_rev: None, - framework_local_dir: Some(Self::aptos_framework_dir()), - skip_fetch_latest_git_deps: false, - }, - bytecode_version: None, - }, - } - .execute() - .await - } - - pub async fn vote( - &self, - index: usize, - proposal_id: u64, - yes: bool, - no: bool, - pool_addresses: Vec, - ) { - SubmitVote { - proposal_id, - yes, - no, - pool_addresses, - txn_options: self.transaction_options(index, None), - } - .execute() - .await - .expect("Successfully voted."); - } - - pub async fn verify_proposal( - &self, - proposal_id: u64, - script_path: &str, - ) -> CliTypedResult { - VerifyProposal { - proposal_id, - compile_proposal_args: CompileScriptFunction { - script_path: Some(script_path.parse().unwrap()), - compiled_script_path: None, - framework_package_args: FrameworkPackageArgs { - framework_git_rev: None, - framework_local_dir: Some(Self::aptos_framework_dir()), - skip_fetch_latest_git_deps: false, - }, - bytecode_version: None, - }, - rest_options: self.rest_options(), - profile: Default::default(), - prompt_options: PromptOptions::yes(), - } - .execute() - .await - } -} - -// ValidatorConfig/ValidatorSet doesn't match Move ValidatorSet struct, -// and json is serialized with different types from both, so hardcoding deserialization. - -fn json_account_to_balance(value: &Value) -> u64 { - u64::from_str( - value - .as_object() - .unwrap() - .get("coin") - .unwrap() - .as_object() - .unwrap() - .get("value") - .unwrap() - .as_str() - .unwrap(), - ) - .unwrap() -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct IndividualValidatorPerformance { - successful_proposals: String, - failed_proposals: String, -} - -impl IndividualValidatorPerformance { - pub fn successful_proposals(&self) -> u32 { - self.successful_proposals.parse().unwrap() - } - - pub fn failed_proposals(&self) -> u32 { - self.failed_proposals.parse().unwrap() - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct ValidatorPerformance { - pub validators: Vec, -} diff --git a/m1/m1-cli/src/test/tests.rs b/m1/m1-cli/src/test/tests.rs deleted file mode 100644 index 62a8d9979..000000000 --- a/m1/m1-cli/src/test/tests.rs +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - move_tool::{ArgWithType, FunctionArgType}, - CliResult, Tool, -}; -use clap::Parser; -use std::str::FromStr; - -/// In order to ensure that there aren't duplicate input arguments for untested CLI commands, -/// we call help on every command to ensure it at least runs -#[tokio::test] -async fn ensure_every_command_args_work() { - assert_cmd_not_panic(&["movement"]).await; - - assert_cmd_not_panic(&["movement", "account"]).await; - assert_cmd_not_panic(&["movement", "account", "create", "--help"]).await; - assert_cmd_not_panic(&["movement", "account", "create-resource-account", "--help"]).await; - assert_cmd_not_panic(&["movement", "account", "fund-with-faucet", "--help"]).await; - assert_cmd_not_panic(&["movement", "account", "list", "--help"]).await; - assert_cmd_not_panic(&["movement", "account", "lookup-address", "--help"]).await; - assert_cmd_not_panic(&["movement", "account", "rotate-key", "--help"]).await; - assert_cmd_not_panic(&["movement", "account", "transfer", "--help"]).await; - - assert_cmd_not_panic(&["movement", "config"]).await; - assert_cmd_not_panic(&["movement", "config", "generate-shell-completions", "--help"]).await; - assert_cmd_not_panic(&["movement", "config", "init", "--help"]).await; - assert_cmd_not_panic(&["movement", "config", "set-global-config", "--help"]).await; - assert_cmd_not_panic(&["movement", "config", "show-global-config"]).await; - assert_cmd_not_panic(&["movement", "config", "show-profiles"]).await; - - assert_cmd_not_panic(&["movement", "genesis"]).await; - assert_cmd_not_panic(&["movement", "genesis", "generate-genesis", "--help"]).await; - assert_cmd_not_panic(&["movement", "genesis", "generate-keys", "--help"]).await; - assert_cmd_not_panic(&["movement", "genesis", "generate-layout-template", "--help"]).await; - assert_cmd_not_panic(&["movement", "genesis", "set-validator-configuration", "--help"]).await; - assert_cmd_not_panic(&["movement", "genesis", "setup-git", "--help"]).await; - assert_cmd_not_panic(&["movement", "genesis", "generate-admin-write-set", "--help"]).await; - - assert_cmd_not_panic(&["movement", "governance"]).await; - assert_cmd_not_panic(&["movement", "governance", "execute-proposal", "--help"]).await; - assert_cmd_not_panic(&["movement", "governance", "generate-upgrade-proposal", "--help"]).await; - assert_cmd_not_panic(&["movement", "governance", "propose", "--help"]).await; - assert_cmd_not_panic(&["movement", "governance", "vote", "--help"]).await; - - assert_cmd_not_panic(&["movement", "info"]).await; - - assert_cmd_not_panic(&["movement", "init", "--help"]).await; - - assert_cmd_not_panic(&["movement", "key"]).await; - assert_cmd_not_panic(&["movement", "key", "generate", "--help"]).await; - assert_cmd_not_panic(&["movement", "key", "extract-peer", "--help"]).await; - - assert_cmd_not_panic(&["movement", "move"]).await; - assert_cmd_not_panic(&["movement", "move", "clean", "--help"]).await; - assert_cmd_not_panic(&["movement", "move", "compile", "--help"]).await; - assert_cmd_not_panic(&["movement", "move", "compile-script", "--help"]).await; - assert_cmd_not_panic(&["movement", "move", "download", "--help"]).await; - assert_cmd_not_panic(&["movement", "move", "init", "--help"]).await; - assert_cmd_not_panic(&["movement", "move", "list", "--help"]).await; - assert_cmd_not_panic(&["movement", "move", "prove", "--help"]).await; - assert_cmd_not_panic(&["movement", "move", "publish", "--help"]).await; - assert_cmd_not_panic(&["movement", "move", "run", "--help"]).await; - assert_cmd_not_panic(&["movement", "move", "run-script", "--help"]).await; - assert_cmd_not_panic(&["movement", "move", "test", "--help"]).await; - assert_cmd_not_panic(&["movement", "move", "transactional-test", "--help"]).await; - assert_cmd_not_panic(&["movement", "move", "view", "--help"]).await; - - assert_cmd_not_panic(&["movement", "node"]).await; - assert_cmd_not_panic(&["movement", "node", "check-network-connectivity", "--help"]).await; - assert_cmd_not_panic(&["movement", "node", "get-stake-pool", "--help"]).await; - assert_cmd_not_panic(&["movement", "node", "analyze-validator-performance", "--help"]).await; - assert_cmd_not_panic(&["movement", "node", "bootstrap-db-from-backup", "--help"]).await; - assert_cmd_not_panic(&["movement", "node", "initialize-validator", "--help"]).await; - assert_cmd_not_panic(&["movement", "node", "join-validator-set", "--help"]).await; - assert_cmd_not_panic(&["movement", "node", "leave-validator-set", "--help"]).await; - assert_cmd_not_panic(&["movement", "node", "run-local-testnet", "--help"]).await; - assert_cmd_not_panic(&["movement", "node", "show-validator-config", "--help"]).await; - assert_cmd_not_panic(&["movement", "node", "show-validator-set", "--help"]).await; - assert_cmd_not_panic(&["movement", "node", "show-validator-stake", "--help"]).await; - assert_cmd_not_panic(&["movement", "node", "update-consensus-key", "--help"]).await; - assert_cmd_not_panic(&[ - "movement", - "node", - "update-validator-network-addresses", - "--help", - ]) - .await; - - assert_cmd_not_panic(&["movement", "stake"]).await; - assert_cmd_not_panic(&["movement", "stake", "add-stake", "--help"]).await; - assert_cmd_not_panic(&["movement", "stake", "increase-lockup", "--help"]).await; - assert_cmd_not_panic(&["movement", "stake", "initialize-stake-owner", "--help"]).await; - assert_cmd_not_panic(&["movement", "stake", "set-delegated-voter", "--help"]).await; - assert_cmd_not_panic(&["movement", "stake", "set-operator", "--help"]).await; - assert_cmd_not_panic(&["movement", "stake", "unlock-stake", "--help"]).await; - assert_cmd_not_panic(&["movement", "stake", "withdraw-stake", "--help"]).await; -} - -/// Ensure we can parse URLs for args -#[tokio::test] -async fn ensure_can_parse_args_with_urls() { - let result = ArgWithType::from_str("string:https://aptoslabs.com").unwrap(); - matches!(result._ty, FunctionArgType::String); - assert_eq!( - result.arg, - bcs::to_bytes(&"https://aptoslabs.com".to_string()).unwrap() - ); -} - -async fn assert_cmd_not_panic(args: &[&str]) { - // When a command fails, it will have a panic in it due to an improperly setup command - // thread 'main' panicked at 'Command propose: Argument names must be unique, but 'assume-yes' is - // in use by more than one argument or group', ... - - match run_cmd(args).await { - Ok(inner) => assert!( - !inner.contains("panic"), - "Failed to not panic cmd {}: {}", - args.join(" "), - inner - ), - Err(inner) => assert!( - !inner.contains("panic"), - "Failed to not panic cmd {}: {}", - args.join(" "), - inner - ), - } -} - -async fn run_cmd(args: &[&str]) -> CliResult { - let tool: Tool = Tool::try_parse_from(args).map_err(|msg| msg.to_string())?; - tool.execute().await -} diff --git a/m1/m1-cli/src/update/helpers.rs b/m1/m1-cli/src/update/helpers.rs deleted file mode 100644 index b54a0c759..000000000 --- a/m1/m1-cli/src/update/helpers.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::{anyhow, Context, Result}; -use self_update::{backends::github::ReleaseList, cargo_crate_version, version::bump_is_greater}; - -#[derive(Debug)] -pub struct UpdateRequiredInfo { - pub update_required: bool, - pub current_version: String, - pub latest_version: String, - pub latest_version_tag: String, -} - -/// Return information about whether an update is required. -pub fn check_if_update_required(repo_owner: &str, repo_name: &str) -> Result { - // Build a configuration for determining the latest release. - let config = ReleaseList::configure() - .repo_owner(repo_owner) - .repo_name(repo_name) - .build() - .map_err(|e| anyhow!("Failed to build configuration to fetch releases: {:#}", e))?; - - // Get the most recent releases. - let releases = config - .fetch() - .map_err(|e| anyhow!("Failed to fetch releases: {:#}", e))?; - - // Find the latest release of the CLI, in which we filter for the CLI tag. - // If the release isn't in the last 30 items (the default API page size) - // this will fail. See https://github.com/aptos-labs/aptos-core/issues/6411. - let mut releases = releases.into_iter(); - let latest_release = loop { - let release = match releases.next() { - Some(release) => release, - None => return Err(anyhow!("Failed to find latest CLI release")), - }; - if release.version.starts_with("movement-cli-") { - break release; - } - }; - let latest_version_tag = latest_release.version; - let latest_version = latest_version_tag.split("-v").last().unwrap(); - - // Return early if we're up to date already. - let current_version = cargo_crate_version!(); - let update_required = bump_is_greater(current_version, latest_version) - .context("Failed to compare current and latest CLI versions")?; - - Ok(UpdateRequiredInfo { - update_required, - current_version: current_version.to_string(), - latest_version: latest_version.to_string(), - latest_version_tag, - }) -} - -pub enum InstallationMethod { - Source, - Homebrew, - Other, -} - -impl InstallationMethod { - pub fn from_env() -> Result { - // Determine update instructions based on what we detect about the installation. - let exe_path = std::env::current_exe()?; - let installation_method = if exe_path.to_string_lossy().contains("brew") { - InstallationMethod::Homebrew - } else if exe_path.to_string_lossy().contains("target") { - InstallationMethod::Source - } else { - InstallationMethod::Other - }; - Ok(installation_method) - } -} diff --git a/m1/m1-cli/src/update/mod.rs b/m1/m1-cli/src/update/mod.rs deleted file mode 100644 index 17dab6d13..000000000 --- a/m1/m1-cli/src/update/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -mod helpers; -mod tool; - -use helpers::check_if_update_required; -pub use tool::UpdateTool; diff --git a/m1/m1-cli/src/update/tool.rs b/m1/m1-cli/src/update/tool.rs deleted file mode 100644 index 1528ff85d..000000000 --- a/m1/m1-cli/src/update/tool.rs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::{check_if_update_required, helpers::InstallationMethod}; -use crate::common::{ - types::{CliCommand, CliTypedResult}, - utils::cli_build_information, -}; -use anyhow::{anyhow, Context}; -use aptos_build_info::BUILD_OS; -use async_trait::async_trait; -use clap::Parser; -use self_update::{backends::github::Update, cargo_crate_version, Status}; -use std::process::Command; - -/// Update the CLI itself -/// -/// This can be used to update the CLI to the latest version. This is useful if you -/// installed the CLI via the install script / by downloading the binary directly. -#[derive(Debug, Parser)] -pub struct UpdateTool { - /// The owner of the repo to download the binary from. - #[clap(long, default_value = "movemnt")] - repo_owner: String, - - /// The name of the repo to download the binary from. - #[clap(long, default_value = "subnet")] // TODO: update CI/CD to include this binar release in GitHub workflows - repo_name: String, -} - -impl UpdateTool { - // Out of the box this crate assumes that you have releases named a specific way - // with the crate name, version, and target triple in a specific format. We don't - // do this with our releases, we have other GitHub releases beyond just the CLI, - // and we don't build for all major target triples, so we have to do some of the - // work ourselves first to figure out what the latest version of the CLI is and - // which binary to download based on the current OS. Then we can plug that into - // the library which takes care of the rest. - fn update(&self) -> CliTypedResult { - let installation_method = - InstallationMethod::from_env().context("Failed to determine installation method")?; - match installation_method { - InstallationMethod::Source => { - return Err( - anyhow!("Detected this CLI was built from source, refusing to update").into(), - ); - }, - InstallationMethod::Homebrew => { - return Err(anyhow!( - "Detected this CLI comes from homebrew, use `brew upgrade aptos` instead" - ) - .into()); - }, - InstallationMethod::Other => {}, - } - - let info = check_if_update_required(&self.repo_owner, &self.repo_name)?; - if !info.update_required { - return Ok(format!("CLI already up to date (v{})", info.latest_version)); - } - - // Determine the target we should download. This is necessary because we don't - // name our binary releases using the target triples nor do we build specifically - // for all major triples, so we have to generalize to one of the binaries we do - // happen to build. We figure this out based on what system the CLI was built on. - let build_info = cli_build_information(); - let target = match build_info.get(BUILD_OS).context("Failed to determine build info of current CLI")?.as_str() { - "linux-x86_64" => { - // In the case of Linux, which build to use depends on the OpenSSL - // library on the host machine. So we try to determine that here. - // This code below parses the output of the `openssl version` command, - // where the version string is the 1th (0-indexing) item in the string - // when split by whitespace. - let output = Command::new("openssl") - .args(["version"]) - .output(); - let version = match output { - Ok(output) => { - let stdout = String::from_utf8(output.stdout).unwrap(); - stdout.split_whitespace().collect::>()[1].to_string() - }, - Err(e) => { - println!("Failed to determine OpenSSL version, assuming an older version: {:#}", e); - "1.0.0".to_string() - } - }; - // On Ubuntu < 22.04 the bundled OpenSSL is version 1.x.x, whereas on - // 22.04+ it is 3.x.x. Unfortunately if you build the CLI on a system - // with one major version of OpenSSL, you cannot use it on a system - // with a different version. Accordingly, if the current system uses - // OpenSSL 3.x.x, we use the version of the CLI built on a system with - // OpenSSL 3.x.x, meaning Ubuntu 22.04. Otherwise we use the one built - // on 20.04. - if version.starts_with('3') { - "Ubuntu-22.04-x86_64" - } else { - "Ubuntu-x86_64" - } - }, - "macos-x86_64" => "MacOSX-x86_64", - "windows-x86_64" => "Windows-x86_64", - wildcard => return Err(anyhow!("Self-updating is not supported on your OS right now, please download the binary manually: {}", wildcard).into()), - }; - - // Build a new configuration that will direct the library to download the - // binary with the target version tag and target that we determined above. - let config = Update::configure() - .repo_owner(&self.repo_owner) - .repo_name(&self.repo_name) - .bin_name("movement") - .current_version(cargo_crate_version!()) - .target_version_tag(&info.latest_version_tag) - .target(target) - .build() - .map_err(|e| anyhow!("Failed to build self-update configuration: {:#}", e))?; - - // Update the binary. - let result = config - .update() - .map_err(|e| anyhow!("Failed to update Movement CLI: {:#}", e))?; - - let message = match result { - Status::UpToDate(_) => panic!("We should have caught this already"), - Status::Updated(_) => format!( - "Successfully updated from v{} to v{}", - info.current_version, info.latest_version - ), - }; - - Ok(message) - } -} - -#[async_trait] -impl CliCommand for UpdateTool { - fn command_name(&self) -> &'static str { - "Update" - } - - async fn execute(self) -> CliTypedResult { - tokio::task::spawn_blocking(move || self.update()) - .await - .context("Failed to self-update Movement CLI")? - } -} From 658ddeca6e84b48edcf66178a7119f05407392ec Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 4 Dec 2023 12:32:04 -0800 Subject: [PATCH 47/58] chore: defaults. --- m1/tests/e2e/src/tests/mod.rs | 2 +- vendors/aptos-core | 2 +- vendors/sui | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/m1/tests/e2e/src/tests/mod.rs b/m1/tests/e2e/src/tests/mod.rs index c1285fbf9..643fd363a 100644 --- a/m1/tests/e2e/src/tests/mod.rs +++ b/m1/tests/e2e/src/tests/mod.rs @@ -122,7 +122,7 @@ async fn e2e() { blockchain_specs: vec![BlockchainSpec { vm_name: String::from("subnet"), genesis: genesis_file_path.to_string(), - // blockchain_alias : String::from("subnet"), // todo: this doesn't always work oddly enough, need to debug + blockchain_alias : String::from("subnet"), // todo: this doesn't always work oddly enough, need to debug ..Default::default() }], ..Default::default() diff --git a/vendors/aptos-core b/vendors/aptos-core index d3642c1c7..a2a657c27 160000 --- a/vendors/aptos-core +++ b/vendors/aptos-core @@ -1 +1 @@ -Subproject commit d3642c1c708269e500c08c9ef9319c248eb6ae6c +Subproject commit a2a657c27f90bd80bce659f7d19aec31bfd3ff95 diff --git a/vendors/sui b/vendors/sui index 76c79d96c..7332e8e3b 160000 --- a/vendors/sui +++ b/vendors/sui @@ -1 +1 @@ -Subproject commit 76c79d96cb15cbadd579832913e5f8922de46da8 +Subproject commit 7332e8e3bcaf7ffac2812df017202726000be934 From 8c57ffcd966ef28fb3b77116bf8a57fd560c88f0 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 4 Dec 2023 14:21:08 -0800 Subject: [PATCH 48/58] fix: hotfix on CLI faucet. --- vendors/aptos-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendors/aptos-core b/vendors/aptos-core index a2a657c27..0624d1bf2 160000 --- a/vendors/aptos-core +++ b/vendors/aptos-core @@ -1 +1 @@ -Subproject commit a2a657c27f90bd80bce659f7d19aec31bfd3ff95 +Subproject commit 0624d1bf20390f1f9b19a3c4eccf3b9da676aefc From 87ced01baaf362ee024fe00e4b77d29efbf2f2c5 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 4 Dec 2023 14:54:12 -0800 Subject: [PATCH 49/58] fix: updated subnet request proxy. --- m1/subnet-request-proxy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/m1/subnet-request-proxy b/m1/subnet-request-proxy index e538ec536..7d9f23eed 160000 --- a/m1/subnet-request-proxy +++ b/m1/subnet-request-proxy @@ -1 +1 @@ -Subproject commit e538ec536ed845020bdd23268bc8c5a6a2140f91 +Subproject commit 7d9f23eedec5bb8e162f6eed9437ca7aea60af6d From 084c9461c9730740cc50866d568ed1026906064c Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 4 Dec 2023 15:34:05 -0800 Subject: [PATCH 50/58] chore: drop movement-benches since maintainer of that crate is unclear. --- m1/movement-benchmark/Cargo.toml | 30 -- .../benches/transaction_benches.rs | 34 --- m1/movement-benchmark/src/lib.rs | 8 - m1/movement-benchmark/src/main.rs | 90 ------ m1/movement-benchmark/src/measurement.rs | 14 - m1/movement-benchmark/src/transactions.rs | 268 ------------------ 6 files changed, 444 deletions(-) delete mode 100644 m1/movement-benchmark/Cargo.toml delete mode 100644 m1/movement-benchmark/benches/transaction_benches.rs delete mode 100644 m1/movement-benchmark/src/lib.rs delete mode 100644 m1/movement-benchmark/src/main.rs delete mode 100644 m1/movement-benchmark/src/measurement.rs delete mode 100644 m1/movement-benchmark/src/transactions.rs diff --git a/m1/movement-benchmark/Cargo.toml b/m1/movement-benchmark/Cargo.toml deleted file mode 100644 index 4544dad36..000000000 --- a/m1/movement-benchmark/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "movement-benchmark" -description = "movement transaction benchmarks" -version = "0.1.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -aptos-bitvec = { workspace = true } -aptos-crypto = { workspace = true } -aptos-gas = { workspace = true, features = ["testing"] } -aptos-language-e2e-tests = { workspace = true } -aptos-types = { workspace = true } -aptos-vm = { workspace = true } -criterion = { workspace = true } -criterion-cpu-time = { workspace = true } -num_cpus = { workspace = true } -proptest = { workspace = true } -# aptos-transaction-benchmarks = { workspace = true } - -[[bench]] -name = "transaction_benches" -harness = false diff --git a/m1/movement-benchmark/benches/transaction_benches.rs b/m1/movement-benchmark/benches/transaction_benches.rs deleted file mode 100644 index e1394d56b..000000000 --- a/m1/movement-benchmark/benches/transaction_benches.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_language_e2e_tests::account_universe::P2PTransferGen; -use aptos_transaction_benchmarks::{ - measurement::wall_time_measurement, transactions::TransactionBencher, -}; -use criterion::{criterion_group, criterion_main, measurement::Measurement, Criterion}; -use proptest::prelude::*; - -// -// Transaction benchmarks -// - -fn peer_to_peer(c: &mut Criterion) { - c.bench_function("peer_to_peer", |b| { - let bencher = TransactionBencher::new(any_with::((1_000, 1_000_000))); - bencher.bench(b) - }); - - c.bench_function("peer_to_peer_parallel", |b| { - let bencher = TransactionBencher::new(any_with::((1_000, 1_000_000))); - bencher.bench_parallel(b) - }); -} - -criterion_group!( - name = txn_benches; - config = wall_time_measurement().sample_size(10); - targets = peer_to_peer -); - -criterion_main!(txn_benches); diff --git a/m1/movement-benchmark/src/lib.rs b/m1/movement-benchmark/src/lib.rs deleted file mode 100644 index 02c5ef4ac..000000000 --- a/m1/movement-benchmark/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] - -pub mod measurement; -pub mod transactions; diff --git a/m1/movement-benchmark/src/main.rs b/m1/movement-benchmark/src/main.rs deleted file mode 100644 index a53f590b0..000000000 --- a/m1/movement-benchmark/src/main.rs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_language_e2e_tests::account_universe::P2PTransferGen; -use proptest::prelude::*; -use std::env; -use movement_benchmark::transactions::TransactionBencher; - -fn main() { - let args: Vec = env::args().collect(); - let (run_par, run_seq) = if args.len() == 4 { - let bool1 = args[2].parse().unwrap(); - let bool2 = args[3].parse().unwrap(); - (bool1, bool2) - } else { - println!("Usage: cargo run --release main "); - println!("Will run both parallel & sequential by default\n"); - (true, true) - }; - - let bencher = TransactionBencher::new(any_with::((1_000, 1_000_000))); - - let acts = [10000]; - let txns = [1000, 10000, 50000]; - let num_warmups = 2; - let num_runs = 10; - - let mut par_measurements: Vec> = Vec::new(); - let mut seq_measurements: Vec> = Vec::new(); - - let concurrency_level = num_cpus::get(); - - for block_size in txns { - for num_accounts in acts { - let (mut par_tps, mut seq_tps) = bencher.blockstm_benchmark( - num_accounts, - block_size, - run_par, - run_seq, - num_warmups, - num_runs, - concurrency_level, - ); - par_tps.sort(); - seq_tps.sort(); - par_measurements.push(par_tps); - seq_measurements.push(seq_tps); - } - } - - println!("Movement Subnet Transaction Stress Test...\n"); - println!("\nconcurrency_level = {}\n", concurrency_level); - - let mut i = 0; - for block_size in txns { - for num_accounts in acts { - println!( - "PARAMS: num_account = {}, block_size = {}", - num_accounts, block_size - ); - - let mut seq_tps = 1; - if run_seq { - println!("Sequential TPS: {:?}", seq_measurements[i]); - let mut seq_sum = 0; - for m in &seq_measurements[i] { - seq_sum += m; - } - seq_tps = seq_sum / seq_measurements[i].len(); - println!("Avg Sequential TPS = {:?}", seq_tps,); - } - - if run_par { - println!("Parallel TPS: {:?}", par_measurements[i]); - let mut par_sum = 0; - for m in &par_measurements[i] { - par_sum += m; - } - let par_tps = par_sum / par_measurements[i].len(); - println!("Avg Parallel TPS = {:?}", par_tps,); - if run_seq { - println!("Speed up {}x over sequential", par_tps / seq_tps); - } - } - i += 1; - } - println!(); - } -} diff --git a/m1/movement-benchmark/src/measurement.rs b/m1/movement-benchmark/src/measurement.rs deleted file mode 100644 index a70fe366f..000000000 --- a/m1/movement-benchmark/src/measurement.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use criterion::Criterion; -use criterion_cpu_time::PosixTime; - -pub fn cpu_time_measurement() -> Criterion { - Criterion::default().with_measurement(PosixTime::UserAndSystemTime) -} - -pub fn wall_time_measurement() -> Criterion { - Criterion::default() -} diff --git a/m1/movement-benchmark/src/transactions.rs b/m1/movement-benchmark/src/transactions.rs deleted file mode 100644 index 490d2c14a..000000000 --- a/m1/movement-benchmark/src/transactions.rs +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_bitvec::BitVec; -use aptos_crypto::HashValue; -use aptos_language_e2e_tests::{ - account_universe::{AUTransactionGen, AccountUniverseGen}, - executor::FakeExecutor, - gas_costs::TXN_RESERVED, -}; -use aptos_types::{ - block_metadata::BlockMetadata, - on_chain_config::{OnChainConfig, ValidatorSet}, - transaction::Transaction, -}; -use aptos_vm::{block_executor::BlockAptosVM, data_cache::AsMoveResolver}; -use criterion::{measurement::Measurement, BatchSize, Bencher}; -use proptest::{ - collection::vec, - strategy::{Strategy, ValueTree}, - test_runner::TestRunner, -}; - -/// Benchmarking support for transactions. -#[derive(Clone, Debug)] -pub struct TransactionBencher { - num_accounts: usize, - num_transactions: usize, - strategy: S, -} - -impl TransactionBencher -where - S: Strategy, - S::Value: AUTransactionGen, -{ - /// The number of accounts created by default. - pub const DEFAULT_NUM_ACCOUNTS: usize = 100; - /// The number of transactions created by default. - pub const DEFAULT_NUM_TRANSACTIONS: usize = 1000; - - /// Creates a new transaction bencher with default settings. - pub fn new(strategy: S) -> Self { - Self { - num_accounts: Self::DEFAULT_NUM_ACCOUNTS, - num_transactions: Self::DEFAULT_NUM_TRANSACTIONS, - strategy, - } - } - - /// Sets a custom number of accounts. - pub fn num_accounts(&mut self, num_accounts: usize) -> &mut Self { - self.num_accounts = num_accounts; - self - } - - /// Sets a custom number of transactions. - pub fn num_transactions(&mut self, num_transactions: usize) -> &mut Self { - self.num_transactions = num_transactions; - self - } - - /// Runs the bencher. - pub fn bench(&self, b: &mut Bencher) { - b.iter_batched( - || { - TransactionBenchState::with_size( - &self.strategy, - self.num_accounts, - self.num_transactions, - ) - }, - |state| state.execute(), - // The input here is the entire list of signed transactions, so it's pretty large. - BatchSize::LargeInput, - ) - } - - /// Runs the bencher. - pub fn bench_parallel(&self, b: &mut Bencher) { - b.iter_batched( - || { - TransactionBenchState::with_size( - &self.strategy, - self.num_accounts, - self.num_transactions, - ) - }, - |state| state.execute_parallel(), - // The input here is the entire list of signed transactions, so it's pretty large. - BatchSize::LargeInput, - ) - } - - /// Runs the bencher. - pub fn blockstm_benchmark( - &self, - num_accounts: usize, - num_txn: usize, - run_par: bool, - run_seq: bool, - num_warmups: usize, - num_runs: usize, - concurrency_level: usize, - ) -> (Vec, Vec) { - let mut par_tps = Vec::new(); - let mut seq_tps = Vec::new(); - - let total_runs = num_warmups + num_runs; - for i in 0..total_runs { - let state = TransactionBenchState::with_size(&self.strategy, num_accounts, num_txn); - - if i < num_warmups { - println!("WARMUP - ignore results"); - state.execute_blockstm_benchmark(concurrency_level, run_par, run_seq); - } else { - println!( - "RUN benchmark for: num_threads = {}, \ - num_account = {}, \ - block_size = {}", - num_cpus::get(), - num_accounts, - num_txn, - ); - let tps = state.execute_blockstm_benchmark(concurrency_level, run_par, run_seq); - par_tps.push(tps.0); - seq_tps.push(tps.1); - } - } - - (par_tps, seq_tps) - } -} - -struct TransactionBenchState { - // Use the fake executor for now. - // TODO: Hook up the real executor in the future. Here's what needs to be done: - // 1. Provide a way to construct a write set from the genesis write set + initial balances. - // 2. Provide a trait for an executor with the functionality required for account_universe. - // 3. Implement the trait for the fake executor. - // 4. Implement the trait for the real executor, using the genesis write set implemented in 1 - // and the helpers in the execution_tests crate. - // 5. Add a type parameter that implements the trait here and switch "executor" to use it. - // 6. Add an enum to TransactionBencher that lets callers choose between the fake and real - // executors. - executor: FakeExecutor, - transactions: Vec, -} - -impl TransactionBenchState { - /// Creates a new benchmark state with the given number of accounts and transactions. - fn with_size(strategy: S, num_accounts: usize, num_transactions: usize) -> Self - where - S: Strategy, - S::Value: AUTransactionGen, - { - let mut state = Self::with_universe( - strategy, - universe_strategy(num_accounts, num_transactions), - num_transactions, - ); - - // Insert a blockmetadata transaction at the beginning to better simulate the real life traffic. - let validator_set = - ValidatorSet::fetch_config(&state.executor.get_state_view().as_move_resolver()) - .expect("Unable to retrieve the validator set from storage"); - - let new_block = BlockMetadata::new( - HashValue::zero(), - 0, - 0, - *validator_set.payload().next().unwrap().account_address(), - BitVec::with_num_bits(validator_set.num_validators() as u16).into(), - vec![], - 1, - ); - - state - .transactions - .insert(0, Transaction::BlockMetadata(new_block)); - - state - } - - /// Creates a new benchmark state with the given account universe strategy and number of - /// transactions. - fn with_universe( - strategy: S, - universe_strategy: impl Strategy, - num_transactions: usize, - ) -> Self - where - S: Strategy, - S::Value: AUTransactionGen, - { - let mut runner = TestRunner::default(); - let universe = universe_strategy - .new_tree(&mut runner) - .expect("creating a new value should succeed") - .current(); - - let mut executor = FakeExecutor::from_head_genesis(); - // Run in gas-cost-stability mode for now -- this ensures that new accounts are ignored. - // XXX We may want to include new accounts in case they have interesting performance - // characteristics. - let mut universe = universe.setup_gas_cost_stability(&mut executor); - - let transaction_gens = vec(strategy, num_transactions) - .new_tree(&mut runner) - .expect("creating a new value should succeed") - .current(); - let transactions = transaction_gens - .into_iter() - .map(|txn_gen| Transaction::UserTransaction(txn_gen.apply(&mut universe).0)) - .collect(); - - Self { - executor, - transactions, - } - } - - /// Executes this state in a single block. - fn execute(self) { - // The output is ignored here since we're just testing transaction performance, not trying - // to assert correctness. - BlockAptosVM::execute_block(self.transactions, self.executor.get_state_view(), 1) - .expect("VM should not fail to start"); - } - - /// Executes this state in a single block via parallel execution. - fn execute_parallel(self) { - // The output is ignored here since we're just testing transaction performance, not trying - // to assert correctness. - BlockAptosVM::execute_block( - self.transactions, - self.executor.get_state_view(), - num_cpus::get(), - ) - .expect("VM should not fail to start"); - } - - fn execute_blockstm_benchmark( - self, - concurrency_level: usize, - run_par: bool, - run_seq: bool, - ) -> (usize, usize) { - BlockAptosVM::execute_block_benchmark( - self.transactions, - self.executor.get_state_view(), - concurrency_level, - run_par, - run_seq, - ) - } -} - -/// Returns a strategy for the account universe customized for benchmarks, i.e. having -/// sufficiently large balance for gas. -fn universe_strategy( - num_accounts: usize, - num_transactions: usize, -) -> impl Strategy { - let balance = TXN_RESERVED * num_transactions as u64 * 5; - AccountUniverseGen::strategy(num_accounts, balance..(balance + 1)) -} From 78c6e8ec84d5ffec3c2f8ab2a94a36e6fd135c1d Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 4 Dec 2023 16:23:47 -0800 Subject: [PATCH 51/58] cicd: better support for windows, macos-x86_64, and stage/main support for macos-aarch64. --- .../actions/setup-linux-aarch64/action.yml | 23 +++++ .../actions/setup-macos-aarch64/action.yml | 30 +++++++ .github/actions/setup-macos-x86_64/action.yml | 30 +++++++ .../actions/setup-windows-x86_64/action.yml | 26 ++++++ .github/workflows/check.yml | 2 + .github/workflows/release.yml | 85 ++++--------------- 6 files changed, 127 insertions(+), 69 deletions(-) create mode 100644 .github/actions/setup-linux-aarch64/action.yml create mode 100644 .github/actions/setup-macos-aarch64/action.yml create mode 100644 .github/actions/setup-macos-x86_64/action.yml create mode 100644 .github/actions/setup-windows-x86_64/action.yml diff --git a/.github/actions/setup-linux-aarch64/action.yml b/.github/actions/setup-linux-aarch64/action.yml new file mode 100644 index 000000000..0e32d916e --- /dev/null +++ b/.github/actions/setup-linux-aarch64/action.yml @@ -0,0 +1,23 @@ +name: 'Setup Linux x86_64' +description: 'Sets up the environment for Linux x86_64 builds with Rust' + +runs: + using: 'composite' + steps: + + - name: Install build essentials + run: | + sudo apt-get update + sudo apt-get install -y build-essential lld libpq-dev unzip + shell: bash + + - name: Install Protoc + uses: arduino/setup-protoc@v1.3.0 + with: + version: "3.20.1" + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.70 diff --git a/.github/actions/setup-macos-aarch64/action.yml b/.github/actions/setup-macos-aarch64/action.yml new file mode 100644 index 000000000..7ba998b02 --- /dev/null +++ b/.github/actions/setup-macos-aarch64/action.yml @@ -0,0 +1,30 @@ +name: 'Setup macOS x86_64' +description: 'Sets up the environment for macOS builds with Rust' + +runs: + using: 'composite' + steps: + + - name: Install build essentials + run: | + brew update + brew install llvm postgresql unzip + shell: bash + + - name: Install OpenSSL + run: brew install openssl + shell: bash + + - name: Install Protoc + uses: arduino/setup-protoc@v1.3.0 + with: + version: "3.20.1" + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.70 + + - name: Export LLVM path + run: echo "LLVM_SYS_130_PREFIX=$(brew --prefix llvm)" >> $GITHUB_ENV diff --git a/.github/actions/setup-macos-x86_64/action.yml b/.github/actions/setup-macos-x86_64/action.yml new file mode 100644 index 000000000..7ba998b02 --- /dev/null +++ b/.github/actions/setup-macos-x86_64/action.yml @@ -0,0 +1,30 @@ +name: 'Setup macOS x86_64' +description: 'Sets up the environment for macOS builds with Rust' + +runs: + using: 'composite' + steps: + + - name: Install build essentials + run: | + brew update + brew install llvm postgresql unzip + shell: bash + + - name: Install OpenSSL + run: brew install openssl + shell: bash + + - name: Install Protoc + uses: arduino/setup-protoc@v1.3.0 + with: + version: "3.20.1" + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.70 + + - name: Export LLVM path + run: echo "LLVM_SYS_130_PREFIX=$(brew --prefix llvm)" >> $GITHUB_ENV diff --git a/.github/actions/setup-windows-x86_64/action.yml b/.github/actions/setup-windows-x86_64/action.yml new file mode 100644 index 000000000..08158f5e5 --- /dev/null +++ b/.github/actions/setup-windows-x86_64/action.yml @@ -0,0 +1,26 @@ +name: 'Setup Windows x86_64' +description: 'Sets up the environment for Windows builds with Rust' + +runs: + using: 'composite' + steps: + + - name: Install build essentials + run: | + choco install llvm postgresql unzip + shell: pwsh + + - name: Install OpenSSL + run: choco install openssl + shell: pwsh + + - name: Install Protoc + uses: arduino/setup-protoc@v1.3.0 + with: + version: "3.20.1" + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.70 diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 146b75fbc..7d9fff1eb 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -22,6 +22,7 @@ jobs: # for testing purposes - name: Trigger test + if: false uses: ./.github/actions/trigger-workflow with: workflowFileName: test.yml @@ -29,6 +30,7 @@ jobs: # for testing purposes - name: Trigger release + if: false uses: ./.github/actions/trigger-workflow with: workflowFileName: release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d85626bd4..df3483a2f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -145,27 +145,18 @@ jobs: submodules: 'recursive' token: ${{ secrets.CI_PAT }} - - name: Install Dependencies - run: | - sudo apt-get update - sudo apt-get install -y build-essential - - - name: Set up Rust for Mac - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: 1.70.0 - target: x86_64-apple-darwin + - name: Setup + uses: ./.github/actions/setup-macos-x86_64 - name: Build Mac binaries run: | cd "$GITHUB_WORKSPACE/m1" - cargo build --release --target x86_64-apple-darwin + cargo build --release - name: Build Mac binaries run: | cd "$GITHUB_WORKSPACE/movement-sdk" - cargo build --release --target x86_64-apple-darwin + cargo build --release - name: Upload subnet uses: actions/upload-release-asset@v1 @@ -200,16 +191,8 @@ jobs: submodules: 'recursive' token: ${{ secrets.CI_PAT }} - - name: Install Dependencies - run: | - sudo apt-get update - sudo apt-get install -y build-essential - - - name: Set up Rust for Mac - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: 1.70.0 + - name: Setup + uses: ./.github/actions/setup-linux-aarch64 - name: Build Mac binaries run: | @@ -242,10 +225,10 @@ jobs: asset_content_type: application/octet-stream pre-release-aarch64-mac: - if: true # testing + # this is expensive, so only run on stage and main + if: github.ref == 'refs/heads/stage' || github.ref == 'refs/heads/main' needs: prepare-release - runs-on: - labels: macos-latest + runs-on: macos-latest-xlarge steps: - name: Checkout Repository uses: actions/checkout@v2 @@ -253,43 +236,14 @@ jobs: submodules: 'recursive' token: ${{ secrets.CI_PAT }} - - name: Set up Rust for Mac ARM64 - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: 1.70.0 - target: aarch64-apple-darwin + - name: Setup + uses: ./.github/actions/setup-macos-aarch64 - name: Build Mac ARM64 binaries run: | cd "$GITHUB_WORKSPACE/m1" cargo build --release --target aarch64-apple-darwin - - - name: Build Mac binaries - run: | - cd "$GITHUB_WORKSPACE/movement-sdk" - cargo build --release --target aarch64-apple-darwin - - - name: Upload subnet - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ needs.prepare-release.outputs.release_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this reference for more info: https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsid - asset_path: ./m1/target/release/subnet - asset_name: subnet-aarch64-mac - asset_content_type: application/octet-stream - - - name: Upload movement - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ needs.prepare-release.outputs.release_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this reference for more info: https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsid - asset_path: ./movement-sdk/target/release/movement - asset_name: movement-aarch64-mac - asset_content_type: application/octet-stream - + pre-release-x86_64-windows: if: true # testing @@ -302,17 +256,10 @@ jobs: with: submodules: 'recursive' token: ${{ secrets.CI_PAT }} - - - name: Add mingw - run: apt-get install -y mingw-w64 - - - name: Set up Rust for Windows - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: 1.70.0 - target: x86_64-pc-windows-gnu - + + - name: Setup Windows + uses: ./.github/actions/setup-windows-x86_64 + - name: Build Windows binaries run: | cd "$GITHUB_WORKSPACE/m1" From f9845252f63d6ea164ac1f18000a5c772dc9c5e9 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 4 Dec 2023 16:27:13 -0800 Subject: [PATCH 52/58] fix: dropping movement-benchmark from subnet tests. --- m1/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/m1/Cargo.toml b/m1/Cargo.toml index cb0d810f5..1e3f93a8e 100644 --- a/m1/Cargo.toml +++ b/m1/Cargo.toml @@ -2,7 +2,6 @@ resolver = "2" members = [ "subnet", - "movement-benchmark", "tests/e2e" ] From 184d8ac704079169ed9351d317c043610d639e0b Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 4 Dec 2023 19:03:26 -0800 Subject: [PATCH 53/58] fix: aliasing is not reliable. --- m1/scripts/build.debug.sh | 2 +- m1/scripts/build.release.sh | 2 +- m1/tests/e2e/src/tests/mod.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/m1/scripts/build.debug.sh b/m1/scripts/build.debug.sh index 024d11299..d4b63c7c7 100755 --- a/m1/scripts/build.debug.sh +++ b/m1/scripts/build.debug.sh @@ -18,4 +18,4 @@ cargo build -p subnet --bin subnet ./target/debug/subnet --help ./target/debug/subnet genesis "hello world" -./target/debug/subnet vm-id timestampvm \ No newline at end of file +./target/debug/subnet vm-id subnet \ No newline at end of file diff --git a/m1/scripts/build.release.sh b/m1/scripts/build.release.sh index 7d596856f..69e447d27 100755 --- a/m1/scripts/build.release.sh +++ b/m1/scripts/build.release.sh @@ -20,4 +20,4 @@ cargo build -p subnet \ ./target/release/subnet --help ./target/release/subnet genesis "hello world" -./target/release/subnet vm-id timestampvm +./target/release/subnet vm-id subnet diff --git a/m1/tests/e2e/src/tests/mod.rs b/m1/tests/e2e/src/tests/mod.rs index 643fd363a..c1285fbf9 100644 --- a/m1/tests/e2e/src/tests/mod.rs +++ b/m1/tests/e2e/src/tests/mod.rs @@ -122,7 +122,7 @@ async fn e2e() { blockchain_specs: vec![BlockchainSpec { vm_name: String::from("subnet"), genesis: genesis_file_path.to_string(), - blockchain_alias : String::from("subnet"), // todo: this doesn't always work oddly enough, need to debug + // blockchain_alias : String::from("subnet"), // todo: this doesn't always work oddly enough, need to debug ..Default::default() }], ..Default::default() From e0d442df7d02b8561b145b990fe421520a8ee9c5 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 7 Dec 2023 03:32:39 -1000 Subject: [PATCH 54/58] feat: docs galore. --- canonical/README.md | 2 + movement-sdk/Cargo.lock | 7 + movement-sdk/Cargo.toml | 20 +- .../canonical-sui-backing-store/Cargo.toml | 0 .../canonical-sui-backing-store/README.md | 8 + .../canonical-sui-backing-store/src/lib.rs | 495 ++++++++++++++++++ movement-sdk/execution/README.md | 2 + .../execution/sui-block-executor/Cargo.toml | 27 + .../execution/sui-block-executor/README.md | 114 ++++ .../execution/sui-block-executor/src/lib.rs | 2 + .../src/sui_block_executor.rs | 20 + .../types/sui-helper-types/Cargo.toml | 11 + .../types/sui-helper-types/src/block/block.rs | 14 + .../types/sui-helper-types/src/block/mod.rs | 2 + .../types/sui-helper-types/src/lib.rs | 2 + .../sui-helper-types/src/providers/mod.rs | 0 16 files changed, 725 insertions(+), 1 deletion(-) create mode 100644 movement-sdk/canonical/canonical-sui-backing-store/Cargo.toml create mode 100644 movement-sdk/canonical/canonical-sui-backing-store/README.md create mode 100644 movement-sdk/canonical/canonical-sui-backing-store/src/lib.rs create mode 100644 movement-sdk/execution/README.md create mode 100644 movement-sdk/execution/sui-block-executor/Cargo.toml create mode 100644 movement-sdk/execution/sui-block-executor/README.md create mode 100644 movement-sdk/execution/sui-block-executor/src/lib.rs create mode 100644 movement-sdk/execution/sui-block-executor/src/sui_block_executor.rs create mode 100644 movement-sdk/types/sui-helper-types/Cargo.toml create mode 100644 movement-sdk/types/sui-helper-types/src/block/block.rs create mode 100644 movement-sdk/types/sui-helper-types/src/block/mod.rs create mode 100644 movement-sdk/types/sui-helper-types/src/lib.rs create mode 100644 movement-sdk/types/sui-helper-types/src/providers/mod.rs diff --git a/canonical/README.md b/canonical/README.md index 365a6eed7..a30bcb98a 100644 --- a/canonical/README.md +++ b/canonical/README.md @@ -1,4 +1,6 @@ # Canonical Execution Layer and Supporting Layers +DEPRECATED: This workspace is deprecated. Please see the work out of the `movement-sdk` workspace. + © 2023, Movement Labs. All rights reserved. diff --git a/movement-sdk/Cargo.lock b/movement-sdk/Cargo.lock index 96b165922..bef15ce4f 100644 --- a/movement-sdk/Cargo.lock +++ b/movement-sdk/Cargo.lock @@ -13129,6 +13129,13 @@ dependencies = [ "workspace-hack", ] +[[package]] +name = "sui-helper-types" +version = "0.1.0" +dependencies = [ + "sui-types", +] + [[package]] name = "sui-json" version = "0.0.0-canonical-sui" diff --git a/movement-sdk/Cargo.toml b/movement-sdk/Cargo.toml index f63647f2c..9a8de6441 100644 --- a/movement-sdk/Cargo.toml +++ b/movement-sdk/Cargo.toml @@ -4,6 +4,9 @@ members = [ "movement-sdk", # "movement-sdk-avalanche", + # types + "types/sui-helper-types", + # clis # "clis/aptos", "clis/movement" @@ -44,4 +47,19 @@ clap = { version = "4.4.10", features = ["derive", "env", "suggestions"] } const-str = "0.5" -tracing = { version = "0.1.27", features = ["span_event"] } \ No newline at end of file +tracing = { version = "0.1.27", features = ["span_event"] } + +# aptos +aptos-framework = { path = "../vendors/aptos-core/aptos-move/framework" } +aptos-vm = { path = "../vendors/aptos-core/aptos-move/aptos-vm" } +aptos-types = { path = "../vendors/aptos-core/types" } +aptos-executor = { path = "../vendors/aptos-core/execution/executor" } +aptos-executor-types = { path = "../vendors/aptos-core/execution/executor-types" } +aptos-storage-interface = { path = "../vendors/aptos-core/storage/storage-interface" } + +# sui +sui-adapter-latest = { path = "../vendors/sui/sui-execution/latest/sui-adapter" } +sui-types = { path = "../vendors/sui/crates/sui-types" } +sui-core = { path = "../vendors/sui/crates/sui-core" } +sui-swarm-config = { path = "../vendors/sui/crates/sui-swarm-config" } +sui-test-transaction-builder = { path = "../vendors/sui/crates/sui-test-transaction-builder" } \ No newline at end of file diff --git a/movement-sdk/canonical/canonical-sui-backing-store/Cargo.toml b/movement-sdk/canonical/canonical-sui-backing-store/Cargo.toml new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/canonical/canonical-sui-backing-store/README.md b/movement-sdk/canonical/canonical-sui-backing-store/README.md new file mode 100644 index 000000000..c6a7809cc --- /dev/null +++ b/movement-sdk/canonical/canonical-sui-backing-store/README.md @@ -0,0 +1,8 @@ +# `canonical-sui-backing-store` +A crate providing a RocksDB-based implementation of the Sui backing store. + +- The original concept for this backing store was sourced from MystenLab's in memory store implementation: https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/simulacrum/src/store/in_mem_store.rs + + +## Challenges +- RocksDB relies on a native library librocksys. Versions of RocksDB in Sui, Aptos, and any other crate used within this workspace must remain in sync. \ No newline at end of file diff --git a/movement-sdk/canonical/canonical-sui-backing-store/src/lib.rs b/movement-sdk/canonical/canonical-sui-backing-store/src/lib.rs new file mode 100644 index 000000000..ab7fd5f6a --- /dev/null +++ b/movement-sdk/canonical/canonical-sui-backing-store/src/lib.rs @@ -0,0 +1,495 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use move_binary_format::CompiledModule; +use move_bytecode_utils::module_cache::GetModule; +use move_core_types::{language_storage::ModuleId, resolver::ModuleResolver}; +use std::collections::{BTreeMap, HashMap}; +use sui_config::genesis; +use sui_types::storage::{get_module, load_package_object_from_object_store, PackageObject}; +use sui_types::{ + base_types::{AuthorityName, ObjectID, SequenceNumber, SuiAddress}, + committee::{Committee, EpochId}, + crypto::{AccountKeyPair, AuthorityKeyPair}, + digests::{ObjectDigest, TransactionDigest, TransactionEventsDigest}, + effects::{TransactionEffects, TransactionEffectsAPI, TransactionEvents}, + error::SuiError, + messages_checkpoint::{ + CheckpointContents, CheckpointContentsDigest, CheckpointDigest, CheckpointSequenceNumber, + VerifiedCheckpoint, + }, + object::{Object, Owner}, + storage::{BackingPackageStore, ChildObjectResolver, ObjectStore, ParentSync}, + transaction::VerifiedTransaction, +}; + +use super::SimulatorStore; + +#[derive(Debug, Default)] +pub struct InMemoryStore { + // Checkpoint data + checkpoints: BTreeMap, + checkpoint_digest_to_sequence_number: HashMap, + checkpoint_contents: HashMap, + + // Transaction data + transactions: HashMap, + effects: HashMap, + events: HashMap, + // Map from transaction digest to events digest for easy lookup + events_tx_digest_index: HashMap, + + // Committee data + epoch_to_committee: Vec, + + // Object data + live_objects: HashMap, + objects: HashMap>, +} + +impl InMemoryStore { + pub fn new(genesis: &genesis::Genesis) -> Self { + let mut store = Self::default(); + store.init_with_genesis(genesis); + store + } + + pub fn get_checkpoint_by_sequence_number( + &self, + sequence_number: CheckpointSequenceNumber, + ) -> Option<&VerifiedCheckpoint> { + self.checkpoints.get(&sequence_number) + } + + pub fn get_checkpoint_by_digest( + &self, + digest: &CheckpointDigest, + ) -> Option<&VerifiedCheckpoint> { + self.checkpoint_digest_to_sequence_number + .get(digest) + .and_then(|sequence_number| self.get_checkpoint_by_sequence_number(*sequence_number)) + } + + pub fn get_highest_checkpint(&self) -> Option<&VerifiedCheckpoint> { + self.checkpoints + .last_key_value() + .map(|(_, checkpoint)| checkpoint) + } + + pub fn get_checkpoint_contents( + &self, + digest: &CheckpointContentsDigest, + ) -> Option<&CheckpointContents> { + self.checkpoint_contents.get(digest) + } + + pub fn get_committee_by_epoch(&self, epoch: EpochId) -> Option<&Committee> { + self.epoch_to_committee.get(epoch as usize) + } + pub fn get_transaction(&self, digest: &TransactionDigest) -> Option<&VerifiedTransaction> { + self.transactions.get(digest) + } + + pub fn get_transaction_effects( + &self, + digest: &TransactionDigest, + ) -> Option<&TransactionEffects> { + self.effects.get(digest) + } + + pub fn get_transaction_events( + &self, + digest: &TransactionEventsDigest, + ) -> Option<&TransactionEvents> { + self.events.get(digest) + } + + pub fn get_object(&self, id: &ObjectID) -> Option<&Object> { + let version = self.live_objects.get(id)?; + self.get_object_at_version(id, *version) + } + + pub fn get_object_at_version(&self, id: &ObjectID, version: SequenceNumber) -> Option<&Object> { + self.objects + .get(id) + .and_then(|versions| versions.get(&version)) + } + + pub fn get_system_state(&self) -> sui_types::sui_system_state::SuiSystemState { + sui_types::sui_system_state::get_sui_system_state(self).expect("system state must exist") + } + + pub fn get_clock(&self) -> sui_types::clock::Clock { + self.get_object(&sui_types::SUI_CLOCK_OBJECT_ID) + .expect("clock should exist") + .to_rust() + .expect("clock object should deserialize") + } + + pub fn owned_objects(&self, owner: SuiAddress) -> impl Iterator { + self.live_objects + .iter() + .flat_map(|(id, version)| self.get_object_at_version(id, *version)) + .filter( + move |object| matches!(object.owner, Owner::AddressOwner(addr) if addr == owner), + ) + } +} + +impl InMemoryStore { + pub fn insert_checkpoint(&mut self, checkpoint: VerifiedCheckpoint) { + if let Some(end_of_epoch_data) = &checkpoint.data().end_of_epoch_data { + let next_committee = end_of_epoch_data + .next_epoch_committee + .iter() + .cloned() + .collect(); + let committee = Committee::new(checkpoint.epoch().saturating_add(1), next_committee); + self.insert_committee(committee); + } + + self.checkpoint_digest_to_sequence_number + .insert(*checkpoint.digest(), *checkpoint.sequence_number()); + self.checkpoints + .insert(*checkpoint.sequence_number(), checkpoint); + } + + pub fn insert_checkpoint_contents(&mut self, contents: CheckpointContents) { + self.checkpoint_contents + .insert(*contents.digest(), contents); + } + + pub fn insert_committee(&mut self, committee: Committee) { + let epoch = committee.epoch as usize; + + if self.epoch_to_committee.get(epoch).is_some() { + return; + } + + if self.epoch_to_committee.len() == epoch { + self.epoch_to_committee.push(committee); + } else { + panic!("committee was inserted into EpochCommitteeMap out of order"); + } + } + + pub fn insert_executed_transaction( + &mut self, + transaction: VerifiedTransaction, + effects: TransactionEffects, + events: TransactionEvents, + written_objects: BTreeMap, + ) { + let deleted_objects = effects.deleted(); + let tx_digest = *effects.transaction_digest(); + self.insert_transaction(transaction); + self.insert_transaction_effects(effects); + self.insert_events(&tx_digest, events); + self.update_objects(written_objects, deleted_objects); + } + + pub fn insert_transaction(&mut self, transaction: VerifiedTransaction) { + self.transactions.insert(*transaction.digest(), transaction); + } + + pub fn insert_transaction_effects(&mut self, effects: TransactionEffects) { + self.effects.insert(*effects.transaction_digest(), effects); + } + + pub fn insert_events(&mut self, tx_digest: &TransactionDigest, events: TransactionEvents) { + self.events_tx_digest_index + .insert(*tx_digest, events.digest()); + self.events.insert(events.digest(), events); + } + + pub fn update_objects( + &mut self, + written_objects: BTreeMap, + deleted_objects: Vec<(ObjectID, SequenceNumber, ObjectDigest)>, + ) { + for (object_id, _, _) in deleted_objects { + self.live_objects.remove(&object_id); + } + + for (object_id, object) in written_objects { + let version = object.version(); + self.live_objects.insert(object_id, version); + self.objects + .entry(object_id) + .or_default() + .insert(version, object); + } + } +} + +impl BackingPackageStore for InMemoryStore { + fn get_package_object( + &self, + package_id: &ObjectID, + ) -> sui_types::error::SuiResult> { + load_package_object_from_object_store(self, package_id) + } +} + +impl ChildObjectResolver for InMemoryStore { + fn read_child_object( + &self, + parent: &ObjectID, + child: &ObjectID, + child_version_upper_bound: SequenceNumber, + ) -> sui_types::error::SuiResult> { + let child_object = match crate::store::SimulatorStore::get_object(self, child) { + None => return Ok(None), + Some(obj) => obj, + }; + + let parent = *parent; + if child_object.owner != Owner::ObjectOwner(parent.into()) { + return Err(SuiError::InvalidChildObjectAccess { + object: *child, + given_parent: parent, + actual_owner: child_object.owner, + }); + } + + if child_object.version() > child_version_upper_bound { + return Err(SuiError::UnsupportedFeatureError { + error: "TODO InMemoryStorage::read_child_object does not yet support bounded reads" + .to_owned(), + }); + } + + Ok(Some(child_object)) + } + + fn get_object_received_at_version( + &self, + owner: &ObjectID, + receiving_object_id: &ObjectID, + receive_object_at_version: SequenceNumber, + _epoch_id: EpochId, + ) -> sui_types::error::SuiResult> { + let recv_object = match crate::store::SimulatorStore::get_object(self, receiving_object_id) + { + None => return Ok(None), + Some(obj) => obj, + }; + if recv_object.owner != Owner::AddressOwner((*owner).into()) { + return Ok(None); + } + + if recv_object.version() != receive_object_at_version { + return Ok(None); + } + Ok(Some(recv_object)) + } +} + +impl GetModule for InMemoryStore { + type Error = SuiError; + type Item = CompiledModule; + + fn get_module_by_id(&self, id: &ModuleId) -> Result, Self::Error> { + Ok(self + .get_module(id)? + .map(|bytes| CompiledModule::deserialize_with_defaults(&bytes).unwrap())) + } +} + +impl ModuleResolver for InMemoryStore { + type Error = SuiError; + + fn get_module(&self, module_id: &ModuleId) -> Result>, Self::Error> { + get_module(self, module_id) + } +} + +impl ObjectStore for InMemoryStore { + fn get_object( + &self, + object_id: &ObjectID, + ) -> Result, sui_types::error::SuiError> { + Ok(self.get_object(object_id).cloned()) + } + + fn get_object_by_key( + &self, + object_id: &ObjectID, + version: sui_types::base_types::VersionNumber, + ) -> Result, sui_types::error::SuiError> { + Ok(self.get_object_at_version(object_id, version).cloned()) + } +} + +impl ParentSync for InMemoryStore { + fn get_latest_parent_entry_ref_deprecated( + &self, + _object_id: ObjectID, + ) -> sui_types::error::SuiResult> { + panic!("Never called in newer protocol versions") + } +} + +#[derive(Debug)] +pub struct KeyStore { + validator_keys: BTreeMap, + #[allow(unused)] + account_keys: BTreeMap, +} + +impl KeyStore { + pub fn from_network_config( + network_config: &sui_swarm_config::network_config::NetworkConfig, + ) -> Self { + use fastcrypto::traits::KeyPair; + + let validator_keys = network_config + .validator_configs() + .iter() + .map(|config| { + ( + config.protocol_public_key(), + config.protocol_key_pair().copy(), + ) + }) + .collect(); + + let account_keys = network_config + .account_keys + .iter() + .map(|key| (key.public().into(), key.copy())) + .collect(); + Self { + validator_keys, + account_keys, + } + } + + pub fn validator(&self, name: &AuthorityName) -> Option<&AuthorityKeyPair> { + self.validator_keys.get(name) + } + + pub fn accounts(&self) -> impl Iterator { + self.account_keys.iter() + } +} + +impl SimulatorStore for InMemoryStore { + fn get_checkpoint_by_sequence_number( + &self, + sequence_number: CheckpointSequenceNumber, + ) -> Option { + self.get_checkpoint_by_sequence_number(sequence_number) + .cloned() + } + + fn get_checkpoint_by_digest(&self, digest: &CheckpointDigest) -> Option { + self.get_checkpoint_by_digest(digest).cloned() + } + + fn get_highest_checkpint(&self) -> Option { + self.get_highest_checkpint().cloned() + } + + fn get_checkpoint_contents( + &self, + digest: &CheckpointContentsDigest, + ) -> Option { + self.get_checkpoint_contents(digest).cloned() + } + + fn get_committee_by_epoch(&self, epoch: EpochId) -> Option { + self.get_committee_by_epoch(epoch).cloned() + } + + fn get_transaction(&self, digest: &TransactionDigest) -> Option { + self.get_transaction(digest).cloned() + } + + fn get_transaction_effects(&self, digest: &TransactionDigest) -> Option { + self.get_transaction_effects(digest).cloned() + } + + fn get_transaction_events( + &self, + digest: &TransactionEventsDigest, + ) -> Option { + self.get_transaction_events(digest).cloned() + } + + fn get_transaction_events_by_tx_digest( + &self, + tx_digest: &TransactionDigest, + ) -> Option { + self.events_tx_digest_index + .get(tx_digest) + .and_then(|x| self.events.get(x)) + .cloned() + } + + fn get_object(&self, id: &ObjectID) -> Option { + self.get_object(id).cloned() + } + + fn get_object_at_version(&self, id: &ObjectID, version: SequenceNumber) -> Option { + self.get_object_at_version(id, version).cloned() + } + + fn get_system_state(&self) -> sui_types::sui_system_state::SuiSystemState { + self.get_system_state() + } + + fn get_clock(&self) -> sui_types::clock::Clock { + self.get_clock() + } + + fn owned_objects(&self, owner: SuiAddress) -> Box + '_> { + Box::new(self.owned_objects(owner).cloned()) + } + + fn insert_checkpoint(&mut self, checkpoint: VerifiedCheckpoint) { + self.insert_checkpoint(checkpoint) + } + + fn insert_checkpoint_contents(&mut self, contents: CheckpointContents) { + self.insert_checkpoint_contents(contents) + } + + fn insert_committee(&mut self, committee: Committee) { + self.insert_committee(committee) + } + + fn insert_executed_transaction( + &mut self, + transaction: VerifiedTransaction, + effects: TransactionEffects, + events: TransactionEvents, + written_objects: BTreeMap, + ) { + self.insert_executed_transaction(transaction, effects, events, written_objects) + } + + fn insert_transaction(&mut self, transaction: VerifiedTransaction) { + self.insert_transaction(transaction) + } + + fn insert_transaction_effects(&mut self, effects: TransactionEffects) { + self.insert_transaction_effects(effects) + } + + fn insert_events(&mut self, tx_digest: &TransactionDigest, events: TransactionEvents) { + self.insert_events(tx_digest, events) + } + + fn update_objects( + &mut self, + written_objects: BTreeMap, + deleted_objects: Vec<(ObjectID, SequenceNumber, ObjectDigest)>, + ) { + self.update_objects(written_objects, deleted_objects) + } + + fn backing_store(&self) -> &dyn sui_types::storage::BackingStore { + self + } +} \ No newline at end of file diff --git a/movement-sdk/execution/README.md b/movement-sdk/execution/README.md new file mode 100644 index 000000000..9fa6b6240 --- /dev/null +++ b/movement-sdk/execution/README.md @@ -0,0 +1,2 @@ +# `execution` +This directory contains the source code for the `execution` layers in the Movement SDK. In the future, these crates should generally export a struct implementing the `ExecutionLayer` trait, however, in early development they will often be used simply for organization \ No newline at end of file diff --git a/movement-sdk/execution/sui-block-executor/Cargo.toml b/movement-sdk/execution/sui-block-executor/Cargo.toml new file mode 100644 index 000000000..fa1f3f3ac --- /dev/null +++ b/movement-sdk/execution/sui-block-executor/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "sui-block" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +# general +anyhow = { workspace = true } +futures = { workspace = true } +rand = { workspace = true } +bcs = { workspace = true } +movement-sdk = { workspace = true } +tempfile = { workspace = true } + +# sui +# todo: conflicting rocksdb means we can't use workspace +# todo: likely movement-sdk will move into its own workspace +# todo: once that happens, we can move sui into its own workspace +# todo: we will have to reconcile the two when we begin on the canonical VM +sui-adapter-latest = { workspace = true } +sui-types = { workspace = true } +sui-core = { workspace = true } +sui-swarm-config = { workspace = true } +sui-test-transaction-builder = { workspace = true} diff --git a/movement-sdk/execution/sui-block-executor/README.md b/movement-sdk/execution/sui-block-executor/README.md new file mode 100644 index 000000000..b9c08f34a --- /dev/null +++ b/movement-sdk/execution/sui-block-executor/README.md @@ -0,0 +1,114 @@ +# `sui-block-executor` +The `sui-block-executor` is a block-based execution layer providing Move VM execution which features the Sui object runtime, Sui-like concurrency, Sui-like side effects, and the ability to integrate with Aptos. + +## Overview +Essentially, the `sui-block-executor` performs the duty of... +1. Transforming [`Vec`](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-types/src/transaction.rs#L2019) into a [`Vec`](https://github.com/MystenLabs/sui/blob/main/crates/sui-types/src/executable_transaction.rs#L55), and then, +2. Executing each element in said vector in deterministic order via [`execute_transaction_to_effects`](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/sui-execution/src/latest.rs#L79). +3. `execute_transaction_to_effects` both returns the `TransactionEffects` and applies them to the backing store which the `AuthorityStore` implements (by implementing the [required traits](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-types/src/storage/mod.rs#L489)). Take a look here to see for example how they are not used directly in the [`AuthorityStore`'s execution_driver](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/execution_driver.rs#L103). However, there's additional complexity here that is addressed below with which we are not yet sure whether we will need to deal. + +> Perhaps the most analogous path in the existing Sui source to what needs to be developed is the [`process_certificate` method from the `AuthorityStore`](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L1339). This is a good point of reference for many parts of this project, hence it is included in the reading material. + +Let's break `sui-block-executor` down. + +### `Vec` +A [`VerifiedExecutableTransaction`](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-types/src/executable_transaction.rs#L14) is two important things: + - `SenderSignedData` which constitutes the actual transactions in a programmable transaction block sent by the user. + - `CertificateEnvelope` which certifies that a transaction belongs in a given epoch. + +Obtaining a `CertificateEnvelope` in an agnostic manner is part of the responsibility of the `sui-block-executor`. In the skeleton, you will see that there is a dependency on a `Box` which is responsible for providing the `CertificateEnvelope` for a given `SenderSignedData`. + +That being said, in the proof concept phase, we will mainly rely on simply transforming `Vec` into `Vec` by certifying each `SenderSignedData` with a `CertificateEnvelope` indicating a system transaction. + +For the near-term goal of obtaining something more meaningful in Avalanche, we will likely want to consider transforming the signature of the data on the block itself. From the current structure--which would not have this data--we would likely use an additional backing store which maps `Vec` to blocks with their completed information as opposed to just `Vec`. + +### The Dependencies of `execute_transaction_to_effects` +The Signature of [`execute_transaction_to_effects` is as follows](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/sui-execution/src/latest.rs#L79): +```rust +fn execute_transaction_to_effects( + &self, + store: &dyn BackingStore, + protocol_config: &ProtocolConfig, + metrics: Arc, + enable_expensive_checks: bool, + certificate_deny_set: &HashSet, + epoch_id: &EpochId, + epoch_timestamp_ms: u64, + input_objects: CheckedInputObjects, + gas_coins: Vec, + gas_status: SuiGasStatus, + transaction_kind: TransactionKind, + transaction_signer: SuiAddress, + transaction_digest: TransactionDigest, +) -> ( + InnerTemporaryStore, + TransactionEffects, + Result<(), ExecutionError>, +) +``` + +- `&dyn BackingStore`: arguably the most important of the parameters. We essentially want to do this over RocksDB on our own. There may be several crates for this developed over time, but `canonical-sui-backing-store` is where we will start. +- `protocol_config`: this is struct pops up all over the Sui crate and was originally a big part of the cause for hesitancy in implementing the Sui block-executor at this level--owing to the presumed entanglement of consensus in parts of the object runtime specifically attributable to this config. However, it turns out that consensus parameters are not in fact used. Instead, what this is [used for at this level](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/sui-execution/v1/sui-adapter/src/programmable_transactions/execution.rs#L639) are things such as... + - shared object deletion settings, + - Move binary format settings, + - Max package size. +- `metrics`: this one we can use [directly from Sui](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-types/src/metrics.rs#L9). +- `enable_expensive_checks`: this is a bool which is used to determine whether to perform expensive checks. We can ignore this for now and set to it whatever works. +- `certificate_deny_set`: this isn't actually used anywhere in Sui's source. We can ignore it and leave it empty. +- `epoch_id`: this is used to determine the epoch id of the transaction. We don't need a notion of epochs right now. But, you'll see that the executor has a `Box` which is used to obtain the epoch id. We can ignore this for now and set it to whatever works. +- `epoch_timestamp_ms`: this is used to determine the epoch timestamp of the transaction. We don't need a notion of epochs right now. But, you'll see that the executor has a `Box` which is used to obtain the epoch timestamp. We can ignore this for now and set it to whatever works. +- `input_objects` : this is slightly more involved and will probably where we actually have to do some optimization after putting something naive together. The input objects can actually be derived from the [transaction](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L963). However, essentially, the reason `execute_transaction_to_effects` requires input objects is because the versions of the objects will have changed from when the transaction was submitted. The `AuthorityStore` handles this by [reading new input objects from its state](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L963). To help us separate concerns here, you'll see that the `sui-block-executor` has a `Box` which is used to obtain the input objects. +- `gas_coins`: this comes from the [transaction data itself](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L1366). +- `gas_status`: this also can come from the [transaction data itself](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-transaction-checks/src/lib.rs#L50). it relies on a reference gas price and protocol config. We currently abstract this away with a `Box`. +- `transaction_kind`: this comes from the [transaction data itself](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L1366). +- `transaction_signer`: this comes from the [transaction data itself](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L1366). +- `transaction_digest`: once we have the `VerifiedExecutableTransaction` we can [obtain the digest from it](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L1363). + + +### Dealing with `TransactionEffects` +The [`AuthorityPerEpochStore`](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority/authority_per_epoch_store.rs#L628) is really the engine of a lot of how Sui execution comes together for the Sui network. However, it unfortunately is also very entangled and asynchronous. + +Though `TransactionEffects` are applied via calling `execute_transaction_to_effects` the `AuthorityPerEpochStore` relies on these effects to inform processes which [affect execution within and between epochs](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority/authority_per_epoch_store.rs#L1172). For the most part, it seems like we can ignore this as we are not concerned with consensus details. However, a second glance should be made at the `AuthorityPerEpochStore` whenever it appears something is not working correctly concerning issues related to the absence, presence, etc. of checkpoint and epoch indicators. + +## Sui Object Runtime +`execute_transaction_to_effects` takes care of the Sui object runtime provided, so long as you are constructing the executor with a MoveVM similar to that produced by [`new_move_vm`](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/sui-execution/src/latest.rs#L55C35-L55C35). In fact `new_move_vm` gives a good entry point for extending the function table, which should be enough for the appropriate integration. + +## Sui-like Concurrency +We need to deterministically group transactions into execution groups that can be safely and maximally executed in parallel based on their sets of shared objects. + +Obtaining shared objects can be accomplished via the transaction directly. + +The simplest way to solve this problem is to brute-force through possible groupings in transaction order which is `$O(n^2 * ||S||)$`. However, practically the sizes of the sets will be much smaller than `||S||` and the number of transactions in a block should be on the order of 100s. + +```python +function find_parallel_groups(sets): + parallel_groups = [] // List to hold groups of parallel sets + for current_set in sets: + added_to_group = False + for group in parallel_groups: + if current_set is disjoint with every set in group: + add current_set to group + added_to_group = True + break + if not added_to_group: + create new group with current_set + add this new group to parallel_groups + return parallel_groups +``` + +> I believe you can do better with a trie algorithm. But, I haven't had time to verify. It would look something like this: +> 1. Insert a null node as the root in the trie. +> 2. Sort all the elements in the shared object sets. +> 3. For each set, insert the elements in the set into the trie. +> 4. Insert the transaction hash as if it were the last element in the set. +> 5. Run each separate subtree from the root in parallel, by doing an in-order traversal to obtain the transaction hash then executing the corresponding transaction. +> The ordering of the elements in the sets ensures that intersecting sets will have the same path/subtree in the trie from the root. +> The complexity should be `$O(n * ||S|| *log(||S||))` I believe, which would be better in almost all cases because `||S||` is likely to be smaller than `n`. However, I haven't had time to verify this. + +A more optimistic alternative is to use something similar to Block-STM with reruns on collisions. However, this can be entertained at a later date. + +## Sui-like Side Effects +`execute_transaction_to_effects` accomplishes a good deal of this. But, we still need to decide upon what to do about checkpoints and epochs. This can be tabled for now. + +## Integration with Aptos +This is actually fairly straightforward on the Sui side. `new_move_vm` allows us to accept the Aptos function table. However, \ No newline at end of file diff --git a/movement-sdk/execution/sui-block-executor/src/lib.rs b/movement-sdk/execution/sui-block-executor/src/lib.rs new file mode 100644 index 000000000..9ed261e18 --- /dev/null +++ b/movement-sdk/execution/sui-block-executor/src/lib.rs @@ -0,0 +1,2 @@ +pub mod sui_block_executor; +pub use sui_block_executor::*; \ No newline at end of file diff --git a/movement-sdk/execution/sui-block-executor/src/sui_block_executor.rs b/movement-sdk/execution/sui-block-executor/src/sui_block_executor.rs new file mode 100644 index 000000000..3931a07ef --- /dev/null +++ b/movement-sdk/execution/sui-block-executor/src/sui_block_executor.rs @@ -0,0 +1,20 @@ +use sui_types::{ + messages_checkpoint::VerifiedCheckpoint, + transaction::{Transaction, VerifiedTransaction}, + executable_transaction::VerifiedExecutableTransaction +}; + +#[derive(Debug, Clone)] +pub struct SuiBlockExecutor { + +} + +pub async fn execute_block(&self, block : Vec)-> Result<(), anyhow::Error> { + + // detect objects in transactions with something similar to: https://github.com/MystenLabs/sui/blob/3af3e0a353ff3ce699a6be9b0ddbf8c05b61892d/crates/sui-core/src/authority.rs#L961 + // assign intersecting transactions to run sequentially in block order + // assign non-intersecting transactions to run in parallel + + todo!(); + +} \ No newline at end of file diff --git a/movement-sdk/types/sui-helper-types/Cargo.toml b/movement-sdk/types/sui-helper-types/Cargo.toml new file mode 100644 index 000000000..94d9c3aeb --- /dev/null +++ b/movement-sdk/types/sui-helper-types/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "sui-helper-types" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +# general +sui-types = { workspace = true } \ No newline at end of file diff --git a/movement-sdk/types/sui-helper-types/src/block/block.rs b/movement-sdk/types/sui-helper-types/src/block/block.rs new file mode 100644 index 000000000..b459e49cb --- /dev/null +++ b/movement-sdk/types/sui-helper-types/src/block/block.rs @@ -0,0 +1,14 @@ +use sui_types::{ + transaction::SenderSignedData, + executable_transaction::VerifiedExecutableTransaction +}; + +/// A SuiBlock is a block as we would most often expect it to be constructed. +/// It contains only user signed data. +#[derive(Debug, Clone)] +pub struct Block(Vec); + +/// A VerifiedBlock is a block that has been verified by the SuiBlockExecutor; it contains `CertificateEnvelope`s for each transaction. +/// In most cases, this should be internally constructed. +#[derive(Debug, Clone)] +pub struct VerifiedExecutableBlock(Vec); \ No newline at end of file diff --git a/movement-sdk/types/sui-helper-types/src/block/mod.rs b/movement-sdk/types/sui-helper-types/src/block/mod.rs new file mode 100644 index 000000000..b870750fb --- /dev/null +++ b/movement-sdk/types/sui-helper-types/src/block/mod.rs @@ -0,0 +1,2 @@ +pub mod block; +pub use block::*; \ No newline at end of file diff --git a/movement-sdk/types/sui-helper-types/src/lib.rs b/movement-sdk/types/sui-helper-types/src/lib.rs new file mode 100644 index 000000000..21f2ffb92 --- /dev/null +++ b/movement-sdk/types/sui-helper-types/src/lib.rs @@ -0,0 +1,2 @@ +pub mod block; +pub mod providers; \ No newline at end of file diff --git a/movement-sdk/types/sui-helper-types/src/providers/mod.rs b/movement-sdk/types/sui-helper-types/src/providers/mod.rs new file mode 100644 index 000000000..e69de29bb From f004c8053f88f5f702fdbbfcd5625ce7a3711ab1 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 7 Dec 2023 05:13:12 -1000 Subject: [PATCH 55/58] chore: improving skeletons. --- movement-sdk/Cargo.lock | 15 +++ movement-sdk/Cargo.toml | 12 +- .../execution/sui-block-executor/Cargo.toml | 18 ++- .../execution/sui-block-executor/README.md | 6 +- .../src/sui_block_executor.rs | 115 ++++++++++++++++-- .../sui-backing-store-rocksdb}/Cargo.toml | 0 .../sui-backing-store-rocksdb}/README.md | 0 .../sui-backing-store-rocksdb}/src/lib.rs | 0 .../sui-block-authority-providers/Cargo.toml | 16 +++ .../sui-block-authority-providers/src/lib.rs | 0 movement-sdk/types/canonical-types/Cargo.toml | 38 ++++++ .../types/canonical-types/src/block/block.rs | 51 ++++++++ .../types/canonical-types/src/block/mod.rs | 1 + movement-sdk/types/canonical-types/src/lib.rs | 2 + .../canonical-types/src/transaction/mod.rs | 1 + .../src/transaction/transaction.rs | 28 +++++ .../types/sui-helper-types/Cargo.toml | 7 +- .../types/sui-helper-types/src/block/block.rs | 18 ++- .../sui-helper-types/src/providers/epoch.rs | 13 ++ .../src/providers/gas_info.rs | 17 +++ .../src/providers/input_object.rs | 10 ++ .../sui-helper-types/src/providers/mod.rs | 5 + .../src/providers/object_version_provider.rs | 8 ++ .../verified_executable_transaction.rs | 9 ++ 24 files changed, 363 insertions(+), 27 deletions(-) rename movement-sdk/{canonical/canonical-sui-backing-store => sui-helpers/sui-backing-store-rocksdb}/Cargo.toml (100%) rename movement-sdk/{canonical/canonical-sui-backing-store => sui-helpers/sui-backing-store-rocksdb}/README.md (100%) rename movement-sdk/{canonical/canonical-sui-backing-store => sui-helpers/sui-backing-store-rocksdb}/src/lib.rs (100%) create mode 100644 movement-sdk/sui-helpers/sui-block-authority-providers/Cargo.toml create mode 100644 movement-sdk/sui-helpers/sui-block-authority-providers/src/lib.rs create mode 100644 movement-sdk/types/canonical-types/Cargo.toml create mode 100644 movement-sdk/types/canonical-types/src/block/block.rs create mode 100644 movement-sdk/types/canonical-types/src/block/mod.rs create mode 100644 movement-sdk/types/canonical-types/src/lib.rs create mode 100644 movement-sdk/types/canonical-types/src/transaction/mod.rs create mode 100644 movement-sdk/types/canonical-types/src/transaction/transaction.rs create mode 100644 movement-sdk/types/sui-helper-types/src/providers/epoch.rs create mode 100644 movement-sdk/types/sui-helper-types/src/providers/gas_info.rs create mode 100644 movement-sdk/types/sui-helper-types/src/providers/input_object.rs create mode 100644 movement-sdk/types/sui-helper-types/src/providers/object_version_provider.rs create mode 100644 movement-sdk/types/sui-helper-types/src/providers/verified_executable_transaction.rs diff --git a/movement-sdk/Cargo.lock b/movement-sdk/Cargo.lock index bef15ce4f..abeaea067 100644 --- a/movement-sdk/Cargo.lock +++ b/movement-sdk/Cargo.lock @@ -12929,6 +12929,18 @@ dependencies = [ "workspace-hack", ] +[[package]] +name = "sui-block" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "movement-sdk", + "sui-helper-types", + "sui-types", + "tokio", +] + [[package]] name = "sui-config" version = "0.0.0-canonical-sui" @@ -13133,6 +13145,9 @@ dependencies = [ name = "sui-helper-types" version = "0.1.0" dependencies = [ + "anyhow", + "async-trait", + "futures", "sui-types", ] diff --git a/movement-sdk/Cargo.toml b/movement-sdk/Cargo.toml index 9a8de6441..8fbefbe1b 100644 --- a/movement-sdk/Cargo.toml +++ b/movement-sdk/Cargo.toml @@ -1,14 +1,17 @@ [workspace] resolver = "2" members = [ + # framework "movement-sdk", # "movement-sdk-avalanche", + # execution + "execution/sui-block-executor", + # types "types/sui-helper-types", # clis - # "clis/aptos", "clis/movement" ] @@ -23,12 +26,17 @@ repository = "https://github.com/movemntdev/m2" rust-version = "1.70" [workspace.dependencies] +# internal +movement-sdk = { path = "movement-sdk" } +sui-helper-types = { path = "types/sui-helper-types" } + + async-trait = { version = "0.1" } anyhow = { version = "1" } # For flexible error handling avalanche-types = { version = "0.1.3", features = ["subnet", "codec_base64"] } -movement-sdk = { path = "movement-sdk" } # aptos = { path = "clis/aptos" } +futures = "0.3" tokio = { version = "1.21.0", features = ["full"] } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["raw_value"] } diff --git a/movement-sdk/execution/sui-block-executor/Cargo.toml b/movement-sdk/execution/sui-block-executor/Cargo.toml index fa1f3f3ac..304f9ce50 100644 --- a/movement-sdk/execution/sui-block-executor/Cargo.toml +++ b/movement-sdk/execution/sui-block-executor/Cargo.toml @@ -6,22 +6,20 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +# internal +movement-sdk = { workspace = true } # general anyhow = { workspace = true } -futures = { workspace = true } -rand = { workspace = true } -bcs = { workspace = true } -movement-sdk = { workspace = true } -tempfile = { workspace = true } +async-trait = { workspace = true } +tokio = { workspace = true } + +# internal +sui-helper-types = { workspace = true } +sui-types = { workspace = true } # sui # todo: conflicting rocksdb means we can't use workspace # todo: likely movement-sdk will move into its own workspace # todo: once that happens, we can move sui into its own workspace # todo: we will have to reconcile the two when we begin on the canonical VM -sui-adapter-latest = { workspace = true } -sui-types = { workspace = true } -sui-core = { workspace = true } -sui-swarm-config = { workspace = true } -sui-test-transaction-builder = { workspace = true} diff --git a/movement-sdk/execution/sui-block-executor/README.md b/movement-sdk/execution/sui-block-executor/README.md index b9c08f34a..baea5d103 100644 --- a/movement-sdk/execution/sui-block-executor/README.md +++ b/movement-sdk/execution/sui-block-executor/README.md @@ -57,7 +57,7 @@ fn execute_transaction_to_effects( - `certificate_deny_set`: this isn't actually used anywhere in Sui's source. We can ignore it and leave it empty. - `epoch_id`: this is used to determine the epoch id of the transaction. We don't need a notion of epochs right now. But, you'll see that the executor has a `Box` which is used to obtain the epoch id. We can ignore this for now and set it to whatever works. - `epoch_timestamp_ms`: this is used to determine the epoch timestamp of the transaction. We don't need a notion of epochs right now. But, you'll see that the executor has a `Box` which is used to obtain the epoch timestamp. We can ignore this for now and set it to whatever works. -- `input_objects` : this is slightly more involved and will probably where we actually have to do some optimization after putting something naive together. The input objects can actually be derived from the [transaction](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L963). However, essentially, the reason `execute_transaction_to_effects` requires input objects is because the versions of the objects will have changed from when the transaction was submitted. The `AuthorityStore` handles this by [reading new input objects from its state](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L963). To help us separate concerns here, you'll see that the `sui-block-executor` has a `Box` which is used to obtain the input objects. +- `input_objects` : this is slightly more involved and will probably be where we actually have to do some optimization after putting something naive together. The input objects can actually be derived from the [transaction](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L963). However, essentially, the reason `execute_transaction_to_effects` requires input objects is because the versions of the objects will have changed from when the transaction was submitted. The `AuthorityStore` handles this by [reading new input objects from its state](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L963). To help us separate concerns here, you'll see that the `sui-block-executor` has a `Box` which is used to obtain the input objects. - `gas_coins`: this comes from the [transaction data itself](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L1366). - `gas_status`: this also can come from the [transaction data itself](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-transaction-checks/src/lib.rs#L50). it relies on a reference gas price and protocol config. We currently abstract this away with a `Box`. - `transaction_kind`: this comes from the [transaction data itself](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L1366). @@ -78,7 +78,7 @@ We need to deterministically group transactions into execution groups that can b Obtaining shared objects can be accomplished via the transaction directly. -The simplest way to solve this problem is to brute-force through possible groupings in transaction order which is `$O(n^2 * ||S||)$`. However, practically the sizes of the sets will be much smaller than `||S||` and the number of transactions in a block should be on the order of 100s. +The simplest way to solve this problem is to brute-force through possible groupings in transaction order which is $O(n^2 * ||S||)$. However, practically the sizes of the sets will be much smaller than $||S||$ and the number of transactions in a block should be on the order of 100s. ```python function find_parallel_groups(sets): @@ -103,7 +103,7 @@ function find_parallel_groups(sets): > 4. Insert the transaction hash as if it were the last element in the set. > 5. Run each separate subtree from the root in parallel, by doing an in-order traversal to obtain the transaction hash then executing the corresponding transaction. > The ordering of the elements in the sets ensures that intersecting sets will have the same path/subtree in the trie from the root. -> The complexity should be `$O(n * ||S|| *log(||S||))` I believe, which would be better in almost all cases because `||S||` is likely to be smaller than `n`. However, I haven't had time to verify this. +> The complexity should be $O(n * ||S|| *log(||S||))$ I believe, which would be better in almost all cases because $||S||$ is likely to be smaller than $n$. However, I haven't had time to verify this. A more optimistic alternative is to use something similar to Block-STM with reruns on collisions. However, this can be entertained at a later date. diff --git a/movement-sdk/execution/sui-block-executor/src/sui_block_executor.rs b/movement-sdk/execution/sui-block-executor/src/sui_block_executor.rs index 3931a07ef..633e072d5 100644 --- a/movement-sdk/execution/sui-block-executor/src/sui_block_executor.rs +++ b/movement-sdk/execution/sui-block-executor/src/sui_block_executor.rs @@ -1,20 +1,115 @@ -use sui_types::{ - messages_checkpoint::VerifiedCheckpoint, - transaction::{Transaction, VerifiedTransaction}, - executable_transaction::VerifiedExecutableTransaction +use sui_helper_types::{ + providers::{ + gas_info::GasInfoProvider, + input_object::InputObjectProvider, + epoch::EpochProvider, + verified_executable_transaction::VerifiedExecutableBlockProvider, + object_version_provider::ObjectVersionProvider + }, + block::{Block, VerifiedExecutableBlock} }; +use movement_sdk::ExecutionLayer; +use sui_types::storage::BackingStore; + +/// Sui block executor struct. +/// ? Feel free to change the ref types to whatever you want. #[derive(Debug, Clone)] pub struct SuiBlockExecutor { + backing_store : Box, + epoch_provider : Box, + gas_info_provider : Box, + input_object_provider : Box, + verified_executable_block_provider : Box, + object_version_provider : Box +} + +impl SuiBlockExecutor { + + /// Creates a new sui block executor. + pub fn new( + backing_store : Box, + epoch_provider : Box, + gas_info_provider : Box, + input_object_provider : Box, + verified_executable_block_provider : Box, + object_version_provider : Box + ) -> Self { + Self { + backing_store, + epoch_provider, + gas_info_provider, + input_object_provider, + verified_executable_block_provider, + object_version_provider + } + } + + async fn execute_transaction_group( + &self, + transaction_group : Vec + ) -> Result<(), anyhow::Error> { + for transaction in transaction_group { + // todo: use execute_transaction_to_effects + } + unimplemented!(); + } } -pub async fn execute_block(&self, block : Vec)-> Result<(), anyhow::Error> { +#[async_trait::async_trait] +impl ExecutionLayer for SuiBlockExecutor { + + type Block = Block; + type BlockId = String; // todo: will update this + type ChangeSet = Option; // todo: will update this + + // Gets the next block from the previous layer. + async fn get_next_block( + &self + ) -> Result, anyhow::Error> { + unimplemented!(); // ? Don't worry about this for now. + } + + // Executes a block and produces a change set. + async fn execute_block( + &self, + block: Self::Block + ) -> Result { - // detect objects in transactions with something similar to: https://github.com/MystenLabs/sui/blob/3af3e0a353ff3ce699a6be9b0ddbf8c05b61892d/crates/sui-core/src/authority.rs#L961 - // assign intersecting transactions to run sequentially in block order - // assign non-intersecting transactions to run in parallel + // transform the block to a verified executable block + let verified_executable_block = self.verified_executable_block_provider.verified_executable_block(&block).await?; - todo!(); + // get the max parallel groups + let max_parallel_groups = verified_executable_block.get_max_parrallel_groups(); + + // set up the object versions for the transactions + let sequencer_parallel_groups = self.object_version_provider.sequence_objects_for_transactions(max_parallel_groups).await?; + + // execute the transaction groups in parallel + futures::future::try_join_all!( + sequencer_parallel_groups.into_iter().map(|transaction_group| self.execute_transaction_group(transaction_group)) + )?; + + unimplemented!(); // ! Worry about this for now. + + } + + // Sends a change set to the next layer, i.e., the storage layer. + async fn send_change_set( + &self, + change_set: Self::ChangeSet + ) -> Result<(), anyhow::Error> { + unimplemented!(); // ? Don't worry about this for now. + } + + // Gets an executed block + async fn get_block( + &self, + block_id: Self::BlockId + ) -> Result, anyhow::Error> { + unimplemented!(); // ? Don't worry about this for now. + } + +} -} \ No newline at end of file diff --git a/movement-sdk/canonical/canonical-sui-backing-store/Cargo.toml b/movement-sdk/sui-helpers/sui-backing-store-rocksdb/Cargo.toml similarity index 100% rename from movement-sdk/canonical/canonical-sui-backing-store/Cargo.toml rename to movement-sdk/sui-helpers/sui-backing-store-rocksdb/Cargo.toml diff --git a/movement-sdk/canonical/canonical-sui-backing-store/README.md b/movement-sdk/sui-helpers/sui-backing-store-rocksdb/README.md similarity index 100% rename from movement-sdk/canonical/canonical-sui-backing-store/README.md rename to movement-sdk/sui-helpers/sui-backing-store-rocksdb/README.md diff --git a/movement-sdk/canonical/canonical-sui-backing-store/src/lib.rs b/movement-sdk/sui-helpers/sui-backing-store-rocksdb/src/lib.rs similarity index 100% rename from movement-sdk/canonical/canonical-sui-backing-store/src/lib.rs rename to movement-sdk/sui-helpers/sui-backing-store-rocksdb/src/lib.rs diff --git a/movement-sdk/sui-helpers/sui-block-authority-providers/Cargo.toml b/movement-sdk/sui-helpers/sui-block-authority-providers/Cargo.toml new file mode 100644 index 000000000..1220781b0 --- /dev/null +++ b/movement-sdk/sui-helpers/sui-block-authority-providers/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "sui-helper-types" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +# sui +sui-types = { workspace = true } + +# general +futures = {workspace = true} +async-trait = { workspace = true } +anyhow = { workspace = true } \ No newline at end of file diff --git a/movement-sdk/sui-helpers/sui-block-authority-providers/src/lib.rs b/movement-sdk/sui-helpers/sui-block-authority-providers/src/lib.rs new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/types/canonical-types/Cargo.toml b/movement-sdk/types/canonical-types/Cargo.toml new file mode 100644 index 000000000..a0bb537b1 --- /dev/null +++ b/movement-sdk/types/canonical-types/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "standalone-sui-subnet" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +avalanche-types = { version = "0.0.398", features = ["subnet", "codec_base64"] } +tokio = { version = "1.25.0", features = ["fs", "rt-multi-thread"] } +tonic = { version = "0.8.3", features = ["gzip"] } +serde = { version = "1.0.152", features = ["derive"] } +serde_json = "1.0.93" # https://github.com/serde-rs/json/releases +serde_with = { version = "2.2.0", features = ["hex"] } +log = "0.4.17" +dirs = "5.0.1" +hex = "0.4.3" +bytes = "1.4.0" +env_logger = "0.10.0" +base64 = { version = "0.21.0" } +chrono = "0.4.23" +derivative = "2.2.0" +jsonrpc-core = "18.0.0" +jsonrpc-core-client = { version = "18.0.0" } +jsonrpc-derive = "18.0.0" +async-channel = "1.9.0" + +anyhow = { workspace = true } +futures = { workspace = true } +rand = { workspace = true } +bcs = { workspace = true } +movement-sdk = { workspace = true } + +# sui +sui-adapter-latest = { workspace = true } +sui-types = { workspace = true } + +aptos-types = { workspace = true} diff --git a/movement-sdk/types/canonical-types/src/block/block.rs b/movement-sdk/types/canonical-types/src/block/block.rs new file mode 100644 index 000000000..943380a06 --- /dev/null +++ b/movement-sdk/types/canonical-types/src/block/block.rs @@ -0,0 +1,51 @@ +// todo: reduce import depth +use crate::transaction::transaction::Transaction; +use aptos_types::transaction::Transaction as AptosTransaction; + +#[derive(Clone, Debug)] +pub struct Block(Vec); + +impl Block { + + pub fn new(transactions: Vec) -> Self { + Block(transactions) + } + + /// Filters transactions to those enums which contain Aptos transactions. + pub fn filter_aptos_transactions(&self) -> Vec { + self.0.iter().filter(|t| t.is_aptos()).cloned().collect() + } + + /// Extracts Aptos transactions refs from the block. + pub fn extract_aptos_transaction_refs(&self) -> Vec<&AptosTransaction> { + self.0.iter().filter_map(|transactions| { + match transactions { + Transaction::Aptos(aptos_transaction) => Some(aptos_transaction), + _ => None + } + }).collect() + } + + /// Extracts Aptos transactions from the block. + pub fn extract_aptos_transactions(&self) -> Vec { + self.0.iter().filter_map(|transactions| { + match transactions { + Transaction::Aptos(aptos_transaction) => Some(aptos_transaction.clone()), + _ => None + } + }).collect() + } + +} + +impl Into> for Block { + fn into(self) -> Vec { + self.0 + } +} + +impl Into for Vec { + fn into(self) -> Block { + Block(self) + } +} \ No newline at end of file diff --git a/movement-sdk/types/canonical-types/src/block/mod.rs b/movement-sdk/types/canonical-types/src/block/mod.rs new file mode 100644 index 000000000..49bc7d4bf --- /dev/null +++ b/movement-sdk/types/canonical-types/src/block/mod.rs @@ -0,0 +1 @@ +pub mod block; \ No newline at end of file diff --git a/movement-sdk/types/canonical-types/src/lib.rs b/movement-sdk/types/canonical-types/src/lib.rs new file mode 100644 index 000000000..cc8af46d5 --- /dev/null +++ b/movement-sdk/types/canonical-types/src/lib.rs @@ -0,0 +1,2 @@ +pub mod transaction; +pub mod block; \ No newline at end of file diff --git a/movement-sdk/types/canonical-types/src/transaction/mod.rs b/movement-sdk/types/canonical-types/src/transaction/mod.rs new file mode 100644 index 000000000..be9dda049 --- /dev/null +++ b/movement-sdk/types/canonical-types/src/transaction/mod.rs @@ -0,0 +1 @@ +pub mod transaction; \ No newline at end of file diff --git a/movement-sdk/types/canonical-types/src/transaction/transaction.rs b/movement-sdk/types/canonical-types/src/transaction/transaction.rs new file mode 100644 index 000000000..a99bbff1d --- /dev/null +++ b/movement-sdk/types/canonical-types/src/transaction/transaction.rs @@ -0,0 +1,28 @@ +use aptos_types::transaction::{Transaction as AptosTransaction}; +use sui_types::transaction::{Transaction as SuiTransaction}; + +#[derive(Clone, Debug)] +pub enum Transaction { + Aptos(AptosTransaction), + Sui(SuiTransaction) +} + +impl Transaction { + + + + pub fn is_aptos(&self) -> bool { + match self { + Transaction::Aptos(_) => true, + Transaction::Sui(_) => false + } + } + + pub fn is_sui(&self) -> bool { + match self { + Transaction::Aptos(_) => false, + Transaction::Sui(_) => true + } + } + +} diff --git a/movement-sdk/types/sui-helper-types/Cargo.toml b/movement-sdk/types/sui-helper-types/Cargo.toml index 94d9c3aeb..1220781b0 100644 --- a/movement-sdk/types/sui-helper-types/Cargo.toml +++ b/movement-sdk/types/sui-helper-types/Cargo.toml @@ -7,5 +7,10 @@ edition = "2021" [dependencies] +# sui +sui-types = { workspace = true } + # general -sui-types = { workspace = true } \ No newline at end of file +futures = {workspace = true} +async-trait = { workspace = true } +anyhow = { workspace = true } \ No newline at end of file diff --git a/movement-sdk/types/sui-helper-types/src/block/block.rs b/movement-sdk/types/sui-helper-types/src/block/block.rs index b459e49cb..633cf213b 100644 --- a/movement-sdk/types/sui-helper-types/src/block/block.rs +++ b/movement-sdk/types/sui-helper-types/src/block/block.rs @@ -11,4 +11,20 @@ pub struct Block(Vec); /// A VerifiedBlock is a block that has been verified by the SuiBlockExecutor; it contains `CertificateEnvelope`s for each transaction. /// In most cases, this should be internally constructed. #[derive(Debug, Clone)] -pub struct VerifiedExecutableBlock(Vec); \ No newline at end of file +pub struct VerifiedExecutableBlock(Vec); + +#[derive(Debug, Clone)] +pub struct VerifiedExecutableExecutionGroups(Vec>); + +impl VerifiedExecutableBlock { + + pub fn new(transactions : Vec) -> Self { + Self(transactions) + } + + pub fn get_max_parrallel_groups(&self) -> VerifiedExecutableExecutionGroups { + unimplemented!(); + } + + +} \ No newline at end of file diff --git a/movement-sdk/types/sui-helper-types/src/providers/epoch.rs b/movement-sdk/types/sui-helper-types/src/providers/epoch.rs new file mode 100644 index 000000000..4366a7638 --- /dev/null +++ b/movement-sdk/types/sui-helper-types/src/providers/epoch.rs @@ -0,0 +1,13 @@ +use sui_types::committee::EpochId; + +#[async_trait::async_trait] +pub trait EpochProvider { + + /// Provides the current epoch id. + async fn epoch_id(&self) -> Result; + + /// Provides the current epoch timestamp. + async fn epoch_timestamp(&self) -> Result; + + +} \ No newline at end of file diff --git a/movement-sdk/types/sui-helper-types/src/providers/gas_info.rs b/movement-sdk/types/sui-helper-types/src/providers/gas_info.rs new file mode 100644 index 000000000..2d65b035a --- /dev/null +++ b/movement-sdk/types/sui-helper-types/src/providers/gas_info.rs @@ -0,0 +1,17 @@ +use sui_types::{ + transaction::{ + InputObjects, + TransactionData + }, + base_types::ObjectRef, + gas::SuiGasStatus +}; + +#[async_trait::async_trait] +pub trait GasInforProvider { + + /// Provides up to date input objects for a transaction. + /// Should be similar to this: https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-transaction-checks/src/lib.rs#L50 + async fn gas_status(&self, transaction_data : &TransactionData, input_objects : &InputObjects, object_refs : &[ObjectRef]) -> Result; + +} \ No newline at end of file diff --git a/movement-sdk/types/sui-helper-types/src/providers/input_object.rs b/movement-sdk/types/sui-helper-types/src/providers/input_object.rs new file mode 100644 index 000000000..7a53d5bd9 --- /dev/null +++ b/movement-sdk/types/sui-helper-types/src/providers/input_object.rs @@ -0,0 +1,10 @@ +use sui_types::transaction::{InputObjects, TransactionData}; + +#[async_trait::async_trait] +pub trait InputObjectProvider { + + /// Provides up to date input objects for a transaction. + /// Should be similar to this: https://github.com/MystenLabs/sui/blob/f52a5db7c4e9343b3b522617bbbc064e9ef3b97f/crates/sui-core/src/transaction_input_loader.rs#L39 + async fn input_objects(&self, transaction_data : &TransactionData) -> Result; + +} \ No newline at end of file diff --git a/movement-sdk/types/sui-helper-types/src/providers/mod.rs b/movement-sdk/types/sui-helper-types/src/providers/mod.rs index e69de29bb..d431a2f87 100644 --- a/movement-sdk/types/sui-helper-types/src/providers/mod.rs +++ b/movement-sdk/types/sui-helper-types/src/providers/mod.rs @@ -0,0 +1,5 @@ +pub mod epoch; +pub mod input_object; +pub mod gas_info; +pub mod verified_executable_transaction; +pub mod object_version_provider; \ No newline at end of file diff --git a/movement-sdk/types/sui-helper-types/src/providers/object_version_provider.rs b/movement-sdk/types/sui-helper-types/src/providers/object_version_provider.rs new file mode 100644 index 000000000..fa474a794 --- /dev/null +++ b/movement-sdk/types/sui-helper-types/src/providers/object_version_provider.rs @@ -0,0 +1,8 @@ +use crate::block::VerifiedExecutableExecutionGroups; + +#[async_trait::async_trait] +pub trait ObjectVersionProvider { + + async fn sequence_objects_for_transactions(&self, transactions : VerifiedExecutableExecutionGroups) -> Result; + +} \ No newline at end of file diff --git a/movement-sdk/types/sui-helper-types/src/providers/verified_executable_transaction.rs b/movement-sdk/types/sui-helper-types/src/providers/verified_executable_transaction.rs new file mode 100644 index 000000000..6f278cda5 --- /dev/null +++ b/movement-sdk/types/sui-helper-types/src/providers/verified_executable_transaction.rs @@ -0,0 +1,9 @@ +use crate::block::{Block, VerifiedExecutableBlock}; + +#[async_trait::async_trait] +pub trait VerifiedExecutableBlockProvider { + + /// Provides a verified executable block. + async fn verified_executable_block(&self, block : &Block) -> Result; + +} \ No newline at end of file From 8b2f3776cc6fe49be96f76a8fec14ba2f96fabc0 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 7 Dec 2023 05:13:38 -1000 Subject: [PATCH 56/58] chore: removing old canonical. --- canonical/.cargo/config.toml | 36 - canonical/.gitignore | 1 - canonical/Cargo.lock | 12159 ---------------- canonical/Cargo.toml | 67 - canonical/README.md | 68 - canonical/canonical-block-executor/Cargo.toml | 48 - .../src/canonical_block_executor/mod.rs | 71 - .../sui_block_executor/README.md | 4 - .../sui_block_executor/mod.rs | 235 - canonical/canonical-block-executor/src/lib.rs | 1 - canonical/canonical-move-natives/Cargo.toml | 40 - canonical/canonical-move-natives/README.md | 7 - .../src/canonical_move_natives/mod.rs | 0 canonical/canonical-move-natives/src/lib.rs | 0 canonical/canonical-move-resolver/Cargo.toml | 43 - canonical/canonical-move-resolver/README.md | 1 - .../src/canonical_move_resolver/aptos.rs | 5 - .../src/canonical_move_resolver/mod.rs | 30 - .../src/canonical_move_resolver/sui.rs | 3 - canonical/canonical-move-resolver/src/lib.rs | 2 - canonical/canonical-move-resolver/src/main.rs | 3 - .../canonical-move-resolver/src/util/mod.rs | 0 canonical/canonical-rpc/Cargo.toml | 40 - canonical/canonical-rpc/README.md | 6 - canonical/canonical-rpc/src/lib.rs | 0 canonical/canonical-types/Cargo.toml | 38 - canonical/canonical-types/src/block/block.rs | 51 - canonical/canonical-types/src/block/mod.rs | 1 - canonical/canonical-types/src/lib.rs | 2 - .../canonical-types/src/transaction/mod.rs | 1 - .../src/transaction/transaction.rs | 28 - canonical/rsc/canonical_shared_resolver.png | Bin 108418 -> 0 bytes canonical/rsc/sui_block_executor.png | Bin 80085 -> 0 bytes canonical/rust-toolchain | 1 - canonical/rustfmt.toml | 11 - 35 files changed, 13003 deletions(-) delete mode 100644 canonical/.cargo/config.toml delete mode 100644 canonical/.gitignore delete mode 100644 canonical/Cargo.lock delete mode 100644 canonical/Cargo.toml delete mode 100644 canonical/README.md delete mode 100644 canonical/canonical-block-executor/Cargo.toml delete mode 100644 canonical/canonical-block-executor/src/canonical_block_executor/mod.rs delete mode 100644 canonical/canonical-block-executor/src/canonical_block_executor/sui_block_executor/README.md delete mode 100644 canonical/canonical-block-executor/src/canonical_block_executor/sui_block_executor/mod.rs delete mode 100644 canonical/canonical-block-executor/src/lib.rs delete mode 100644 canonical/canonical-move-natives/Cargo.toml delete mode 100644 canonical/canonical-move-natives/README.md delete mode 100644 canonical/canonical-move-natives/src/canonical_move_natives/mod.rs delete mode 100644 canonical/canonical-move-natives/src/lib.rs delete mode 100644 canonical/canonical-move-resolver/Cargo.toml delete mode 100644 canonical/canonical-move-resolver/README.md delete mode 100644 canonical/canonical-move-resolver/src/canonical_move_resolver/aptos.rs delete mode 100644 canonical/canonical-move-resolver/src/canonical_move_resolver/mod.rs delete mode 100644 canonical/canonical-move-resolver/src/canonical_move_resolver/sui.rs delete mode 100644 canonical/canonical-move-resolver/src/lib.rs delete mode 100644 canonical/canonical-move-resolver/src/main.rs delete mode 100644 canonical/canonical-move-resolver/src/util/mod.rs delete mode 100644 canonical/canonical-rpc/Cargo.toml delete mode 100644 canonical/canonical-rpc/README.md delete mode 100644 canonical/canonical-rpc/src/lib.rs delete mode 100644 canonical/canonical-types/Cargo.toml delete mode 100644 canonical/canonical-types/src/block/block.rs delete mode 100644 canonical/canonical-types/src/block/mod.rs delete mode 100644 canonical/canonical-types/src/lib.rs delete mode 100644 canonical/canonical-types/src/transaction/mod.rs delete mode 100644 canonical/canonical-types/src/transaction/transaction.rs delete mode 100644 canonical/rsc/canonical_shared_resolver.png delete mode 100644 canonical/rsc/sui_block_executor.png delete mode 100644 canonical/rust-toolchain delete mode 100644 canonical/rustfmt.toml diff --git a/canonical/.cargo/config.toml b/canonical/.cargo/config.toml deleted file mode 100644 index 6b23d9edc..000000000 --- a/canonical/.cargo/config.toml +++ /dev/null @@ -1,36 +0,0 @@ -[alias] -xclippy = [ - "clippy", - "--workspace", - "--all-targets", - "--", - "-Dwarnings", - "-Wclippy::all", - "-Aclippy::upper_case_acronyms", - "-Aclippy::enum-variant-names", - "-Aclippy::result-large-err", - "-Aclippy::mutable-key-type", -] - -[build] -rustflags = ["--cfg", "tokio_unstable", "-C", "force-frame-pointers=yes", "-C", "force-unwind-tables=yes"] - -# TODO(grao): Figure out whether we should enable other cpu features, and whether we should use a different way to configure them rather than list every single one here. -[target.x86_64-unknown-linux-gnu] -rustflags = ["--cfg", "tokio_unstable", "-C", "link-arg=-fuse-ld=lld", "-C", "force-frame-pointers=yes", "-C", "force-unwind-tables=yes", "-C", "target-feature=+sse4.2"] - -# 64 bit MSVC -[target.x86_64-pc-windows-msvc] -rustflags = [ - "--cfg", - "tokio_unstable", - "-C", - "force-frame-pointers=yes", - "-C", - "force-unwind-tables=yes", - "-C", - "link-arg=/STACK:8000000" # Set stack to 8 MB -] - -[target.aarch64-apple-darwin] -rustflags = ["-C", "link-arg=-fuse-ld=/opt/homebrew/opt/llvm/bin/ld64.lld"] \ No newline at end of file diff --git a/canonical/.gitignore b/canonical/.gitignore deleted file mode 100644 index 1de565933..000000000 --- a/canonical/.gitignore +++ /dev/null @@ -1 +0,0 @@ -target \ No newline at end of file diff --git a/canonical/Cargo.lock b/canonical/Cargo.lock deleted file mode 100644 index a281a151d..000000000 --- a/canonical/Cargo.lock +++ /dev/null @@ -1,12159 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" - -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aes" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "ahash" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" -dependencies = [ - "getrandom 0.2.10", - "once_cell", - "version_check", -] - -[[package]] -name = "ahash" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy 0.7.25", -] - -[[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr", -] - -[[package]] -name = "aliasable" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" - -[[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 = "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 = "anemo" -version = "0.0.0" -source = "git+https://github.com/mystenlabs/anemo.git?rev=1169850e6af127397068cd86764c29b1d49dbe35#1169850e6af127397068cd86764c29b1d49dbe35" -dependencies = [ - "anyhow", - "async-trait", - "bincode", - "bytes", - "ed25519", - "futures 0.3.28", - "hex", - "http", - "matchit 0.5.0", - "pin-project-lite", - "pkcs8 0.9.0", - "quinn", - "quinn-proto", - "rand 0.8.5", - "rcgen 0.9.3", - "ring 0.16.20", - "rustls 0.21.8", - "rustls-webpki", - "serde 1.0.190", - "serde_json", - "socket2 0.5.5", - "tap", - "thiserror", - "tokio", - "tokio-util 0.7.10", - "tower", - "tracing", - "x509-parser 0.14.0", -] - -[[package]] -name = "anemo-build" -version = "0.0.0" -source = "git+https://github.com/mystenlabs/anemo.git?rev=1169850e6af127397068cd86764c29b1d49dbe35#1169850e6af127397068cd86764c29b1d49dbe35" -dependencies = [ - "prettyplease 0.1.25", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "anemo-tower" -version = "0.0.0" -source = "git+https://github.com/mystenlabs/anemo.git?rev=1169850e6af127397068cd86764c29b1d49dbe35#1169850e6af127397068cd86764c29b1d49dbe35" -dependencies = [ - "anemo", - "bytes", - "dashmap", - "futures 0.3.28", - "governor", - "nonzero_ext", - "pin-project-lite", - "tokio", - "tower", - "tracing", - "uuid", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "anstream" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" - -[[package]] -name = "anstyle-parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" -dependencies = [ - "anstyle", - "windows-sys 0.48.0", -] - -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" -dependencies = [ - "backtrace", -] - -[[package]] -name = "aptos-aggregator" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "aptos-crypto", - "aptos-state-view", - "aptos-types", - "bcs 0.1.4", - "better_any", - "move-binary-format 0.0.3-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-table-extension", - "once_cell", - "smallvec", -] - -[[package]] -name = "aptos-bitvec" -version = "0.1.0-canonical-aptos" -dependencies = [ - "serde 1.0.190", - "serde_bytes", -] - -[[package]] -name = "aptos-block-executor" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "aptos-aggregator", - "aptos-infallible", - "aptos-logger", - "aptos-metrics-core", - "aptos-mvhashmap", - "aptos-state-view", - "aptos-types", - "aptos-vm-logging", - "arc-swap", - "bcs 0.1.4", - "crossbeam", - "dashmap", - "move-binary-format 0.0.3-canonical-aptos", - "num_cpus", - "once_cell", - "parking_lot 0.12.1", - "rayon", -] - -[[package]] -name = "aptos-block-partitioner" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "aptos-crypto", - "aptos-logger", - "aptos-metrics-core", - "aptos-types", - "bcs 0.1.4", - "clap 4.4.10", - "dashmap", - "itertools 0.10.5", - "move-core-types 0.0.4-canonical-aptos", - "once_cell", - "rand 0.7.3", - "rayon", -] - -[[package]] -name = "aptos-crypto" -version = "0.0.3-canonical-aptos" -dependencies = [ - "anyhow", - "aptos-crypto-derive", - "ark-ec", - "ark-ff", - "ark-std", - "bcs 0.1.4", - "blst", - "bytes", - "curve25519-dalek", - "digest 0.9.0", - "ed25519-dalek", - "hex", - "hkdf 0.10.0", - "libsecp256k1", - "more-asserts", - "once_cell", - "proptest", - "proptest-derive", - "rand 0.7.3", - "rand_core 0.5.1", - "ring 0.16.20", - "serde 1.0.190", - "serde-name 0.1.2", - "serde_bytes", - "sha2 0.10.8", - "sha2 0.9.9", - "static_assertions", - "thiserror", - "tiny-keccak", - "x25519-dalek", -] - -[[package]] -name = "aptos-crypto-derive" -version = "0.0.3-canonical-aptos" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "aptos-framework" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "aptos-aggregator", - "aptos-crypto", - "aptos-gas-algebra-ext", - "aptos-move-stdlib", - "aptos-sdk-builder", - "aptos-state-view", - "aptos-types", - "ark-bls12-381", - "ark-ec", - "ark-ff", - "ark-serialize", - "ark-std", - "base64 0.13.1", - "bcs 0.1.4", - "better_any", - "blake2-rfc", - "blst", - "clap 4.4.10", - "codespan-reporting", - "curve25519-dalek", - "flate2", - "hex", - "include_dir 0.7.3", - "itertools 0.10.5", - "libsecp256k1", - "log", - "move-binary-format 0.0.3-canonical-aptos", - "move-command-line-common 0.1.0-canonical-aptos", - "move-compiler 0.0.1-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-docgen 0.1.0-canonical-aptos", - "move-model 0.1.0-canonical-aptos", - "move-package 0.1.0-canonical-aptos", - "move-prover 0.1.0-canonical-aptos", - "move-prover-boogie-backend 0.1.0-canonical-aptos", - "move-stackless-bytecode 0.1.0-canonical-aptos", - "move-table-extension", - "move-vm-runtime 0.1.0-canonical-aptos", - "move-vm-types 0.1.0-canonical-aptos", - "num-traits 0.2.17", - "once_cell", - "rand 0.7.3", - "rand_core 0.5.1", - "rayon", - "ripemd", - "serde 1.0.190", - "serde_bytes", - "serde_json", - "serde_yaml 0.8.26", - "sha2 0.10.8", - "sha2 0.9.9", - "sha3 0.9.1", - "siphasher", - "smallvec", - "tempfile", - "thiserror", - "tiny-keccak", -] - -[[package]] -name = "aptos-gas" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "aptos-framework", - "aptos-gas-algebra-ext", - "aptos-global-constants", - "aptos-logger", - "aptos-move-stdlib", - "aptos-package-builder", - "aptos-types", - "aptos-vm-types", - "bcs 0.1.4", - "clap 4.4.10", - "move-binary-format 0.0.3-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-model 0.1.0-canonical-aptos", - "move-table-extension", - "move-vm-types 0.1.0-canonical-aptos", -] - -[[package]] -name = "aptos-gas-algebra-ext" -version = "0.0.1-canonical-aptos" -dependencies = [ - "move-core-types 0.0.4-canonical-aptos", -] - -[[package]] -name = "aptos-global-constants" -version = "0.1.0-canonical-aptos" - -[[package]] -name = "aptos-infallible" -version = "0.1.0-canonical-aptos" - -[[package]] -name = "aptos-log-derive" -version = "0.1.0-canonical-aptos" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "aptos-logger" -version = "0.1.0-canonical-aptos" -dependencies = [ - "aptos-infallible", - "aptos-log-derive", - "aptos-node-identity", - "backtrace", - "chrono", - "erased-serde", - "futures 0.3.28", - "hostname", - "once_cell", - "prometheus", - "serde 1.0.190", - "serde_json", - "strum", - "strum_macros", - "tokio", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "aptos-memory-usage-tracker" -version = "0.1.0-canonical-aptos" -dependencies = [ - "aptos-gas", - "aptos-types", - "move-binary-format 0.0.3-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-vm-types 0.1.0-canonical-aptos", -] - -[[package]] -name = "aptos-metrics-core" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "prometheus", -] - -[[package]] -name = "aptos-move-stdlib" -version = "0.1.1-canonical-aptos" -dependencies = [ - "anyhow", - "hex", - "log", - "move-binary-format 0.0.3-canonical-aptos", - "move-command-line-common 0.1.0-canonical-aptos", - "move-compiler 0.0.1-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-docgen 0.1.0-canonical-aptos", - "move-errmapgen 0.1.0-canonical-aptos", - "move-prover 0.1.0-canonical-aptos", - "move-vm-runtime 0.1.0-canonical-aptos", - "move-vm-types 0.1.0-canonical-aptos", - "sha2 0.9.9", - "sha3 0.9.1", - "smallvec", - "walkdir", -] - -[[package]] -name = "aptos-mvhashmap" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "aptos-aggregator", - "aptos-crypto", - "aptos-infallible", - "aptos-types", - "bcs 0.1.4", - "crossbeam", - "dashmap", -] - -[[package]] -name = "aptos-node-identity" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "aptos-types", - "claims", - "hostname", - "once_cell", -] - -[[package]] -name = "aptos-package-builder" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "aptos-framework", - "itertools 0.10.5", - "move-command-line-common 0.1.0-canonical-aptos", - "move-package 0.1.0-canonical-aptos", - "tempfile", -] - -[[package]] -name = "aptos-scratchpad" -version = "0.1.0-canonical-aptos" -dependencies = [ - "aptos-crypto", - "aptos-infallible", - "aptos-metrics-core", - "aptos-types", - "bitvec 1.0.1", - "itertools 0.10.5", - "once_cell", - "rayon", - "thiserror", -] - -[[package]] -name = "aptos-sdk-builder" -version = "0.2.0-canonical-aptos" -dependencies = [ - "anyhow", - "aptos-types", - "bcs 0.1.4", - "clap 4.4.10", - "heck 0.3.3", - "move-core-types 0.0.4-canonical-aptos", - "once_cell", - "regex", - "serde-generate", - "serde-reflection 0.3.5", - "serde_yaml 0.8.26", - "textwrap 0.15.2", -] - -[[package]] -name = "aptos-secure-net" -version = "0.1.0-canonical-aptos" -dependencies = [ - "aptos-logger", - "aptos-metrics-core", - "once_cell", - "serde 1.0.190", - "thiserror", -] - -[[package]] -name = "aptos-speculative-state-helper" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "aptos-infallible", - "crossbeam", - "once_cell", - "rayon", -] - -[[package]] -name = "aptos-state-view" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "aptos-crypto", - "aptos-types", - "bcs 0.1.4", - "serde 1.0.190", - "serde_bytes", - "serde_json", -] - -[[package]] -name = "aptos-storage-interface" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "aptos-crypto", - "aptos-logger", - "aptos-metrics-core", - "aptos-scratchpad", - "aptos-secure-net", - "aptos-state-view", - "aptos-types", - "aptos-vm", - "arr_macro", - "bcs 0.1.4", - "crossbeam-channel", - "dashmap", - "itertools 0.10.5", - "move-core-types 0.0.4-canonical-aptos", - "once_cell", - "parking_lot 0.12.1", - "rayon", - "serde 1.0.190", - "thiserror", -] - -[[package]] -name = "aptos-types" -version = "0.0.3-canonical-aptos" -dependencies = [ - "anyhow", - "aptos-bitvec", - "aptos-crypto", - "aptos-crypto-derive", - "arr_macro", - "bcs 0.1.4", - "chrono", - "derivative", - "hex", - "itertools 0.10.5", - "move-core-types 0.0.4-canonical-aptos", - "move-table-extension", - "num-derive", - "num-traits 0.2.17", - "once_cell", - "rand 0.7.3", - "rayon", - "serde 1.0.190", - "serde_bytes", - "serde_json", - "serde_yaml 0.8.26", - "thiserror", - "tiny-keccak", -] - -[[package]] -name = "aptos-utils" -version = "0.1.0-canonical-aptos" - -[[package]] -name = "aptos-vm" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "aptos-aggregator", - "aptos-block-executor", - "aptos-block-partitioner", - "aptos-crypto", - "aptos-crypto-derive", - "aptos-framework", - "aptos-gas", - "aptos-infallible", - "aptos-logger", - "aptos-memory-usage-tracker", - "aptos-metrics-core", - "aptos-move-stdlib", - "aptos-mvhashmap", - "aptos-state-view", - "aptos-types", - "aptos-utils", - "aptos-vm-logging", - "aptos-vm-types", - "bcs 0.1.4", - "dashmap", - "fail 0.5.1", - "futures 0.3.28", - "move-binary-format 0.0.3-canonical-aptos", - "move-bytecode-utils 0.1.0-canonical-aptos", - "move-bytecode-verifier 0.1.0-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-table-extension", - "move-vm-runtime 0.1.0-canonical-aptos", - "move-vm-test-utils 0.1.0-canonical-aptos", - "move-vm-types 0.1.0-canonical-aptos", - "num_cpus", - "once_cell", - "ouroboros 0.15.6", - "rand 0.7.3", - "rayon", - "serde 1.0.190", - "serde_json", - "smallvec", - "tracing", -] - -[[package]] -name = "aptos-vm-logging" -version = "0.1.0-canonical-aptos" -dependencies = [ - "aptos-crypto", - "aptos-logger", - "aptos-metrics-core", - "aptos-speculative-state-helper", - "aptos-state-view", - "aptos-types", - "arc-swap", - "once_cell", - "serde 1.0.190", -] - -[[package]] -name = "aptos-vm-types" -version = "0.0.1-canonical-aptos" -dependencies = [ - "anyhow", - "aptos-aggregator", - "aptos-state-view", - "aptos-types", - "move-binary-format 0.0.3-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", -] - -[[package]] -name = "arc-swap" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" -dependencies = [ - "serde 1.0.190", -] - -[[package]] -name = "ark-bls12-381" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-serialize", - "ark-std", -] - -[[package]] -name = "ark-bn254" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-std", -] - -[[package]] -name = "ark-crypto-primitives" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3a13b34da09176a8baba701233fdffbaa7c1b1192ce031a3da4e55ce1f1a56" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-relations", - "ark-serialize", - "ark-snark", - "ark-std", - "blake2", - "derivative", - "digest 0.10.7", - "sha2 0.10.8", -] - -[[package]] -name = "ark-ec" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" -dependencies = [ - "ark-ff", - "ark-poly", - "ark-serialize", - "ark-std", - "derivative", - "hashbrown 0.13.2", - "itertools 0.10.5", - "num-traits 0.2.17", - "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", - "ark-ff-macros", - "ark-serialize", - "ark-std", - "derivative", - "digest 0.10.7", - "itertools 0.10.5", - "num-bigint", - "num-traits 0.2.17", - "paste", - "rustc_version", - "zeroize", -] - -[[package]] -name = "ark-ff-asm" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" -dependencies = [ - "quote 1.0.33", - "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 0.2.17", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "ark-groth16" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20ceafa83848c3e390f1cbf124bc3193b3e639b3f02009e0e290809a501b95fc" -dependencies = [ - "ark-crypto-primitives", - "ark-ec", - "ark-ff", - "ark-poly", - "ark-relations", - "ark-serialize", - "ark-std", -] - -[[package]] -name = "ark-poly" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" -dependencies = [ - "ark-ff", - "ark-serialize", - "ark-std", - "derivative", - "hashbrown 0.13.2", -] - -[[package]] -name = "ark-relations" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00796b6efc05a3f48225e59cb6a2cda78881e7c390872d5786aaf112f31fb4f0" -dependencies = [ - "ark-ff", - "ark-std", - "tracing", -] - -[[package]] -name = "ark-secp256r1" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3975a01b0a6e3eae0f72ec7ca8598a6620fc72fa5981f6f5cca33b7cd788f633" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-std", -] - -[[package]] -name = "ark-serialize" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" -dependencies = [ - "ark-serialize-derive", - "ark-std", - "digest 0.10.7", - "num-bigint", -] - -[[package]] -name = "ark-serialize-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "ark-snark" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d3cc6833a335bb8a600241889ead68ee89a3cf8448081fb7694c0fe503da63" -dependencies = [ - "ark-ff", - "ark-relations", - "ark-serialize", - "ark-std", -] - -[[package]] -name = "ark-std" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" -dependencies = [ - "num-traits 0.2.17", - "rand 0.8.5", -] - -[[package]] -name = "arr_macro" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a105bfda48707cf19220129e78fca01e9639433ffaef4163546ed8fb04120a5" -dependencies = [ - "arr_macro_impl", - "proc-macro-hack", -] - -[[package]] -name = "arr_macro_impl" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0609c78bd572f4edc74310dfb63a01f5609d53fa8b4dd7c4d98aef3b3e8d72d1" -dependencies = [ - "proc-macro-hack", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -dependencies = [ - "nodrop", -] - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - -[[package]] -name = "asn1-rs" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" -dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom 7.1.3", - "num-traits 0.2.17", - "rusticata-macros", - "thiserror", - "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 1.0.69", - "quote 1.0.33", - "syn 1.0.109", - "synstructure", -] - -[[package]] -name = "asn1-rs-impl" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - -[[package]] -name = "async-compression" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" -dependencies = [ - "brotli", - "flate2", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener", -] - -[[package]] -name = "async-stream" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "async-task" -version = "4.3.0" -source = "git+https://github.com/mystenmark/async-task?rev=4e45b26e11126b191701b9b2ce5e2346b8d7682f#4e45b26e11126b191701b9b2ce5e2346b8d7682f" - -[[package]] -name = "async-trait" -version = "0.1.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "async_once" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ce4f10ea3abcd6617873bae9f91d1c5332b4a778bd9ce34d0cd517474c1de82" - -[[package]] -name = "atomic_float" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62af46d040ba9df09edc6528dae9d8e49f5f3e82f55b7d2ec31a733c38dbc49d" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "auto_impl" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" -dependencies = [ - "proc-macro-error", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "auto_ops" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7460f7dd8e100147b82a63afca1a20eb6c231ee36b90ba7272e14951cb58af59" - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "autotools" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8da1805e028a172334c3b680f93e71126f2327622faef2ec3d893c0a4ad77" -dependencies = [ - "cc", -] - -[[package]] -name = "avalanche-types" -version = "0.0.398" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94736aaa736be8376620b7dd3714d4c65a6d8740457e76277dd6ad93d19f7e64" -dependencies = [ - "async-trait", - "base64 0.21.5", - "bech32", - "blst", - "bs58 0.5.0", - "bytes", - "cert-manager", - "chrono", - "cmp-manager", - "ecdsa 0.16.8", - "ethers-core", - "futures 0.3.28", - "hex", - "hmac 0.12.1", - "http", - "hyper", - "jsonrpc-core", - "k256 0.13.1", - "lazy_static 1.4.0", - "log", - "num-derive", - "num-traits 0.2.17", - "prefix-manager", - "primitive-types 0.12.2", - "prost 0.11.9", - "protoc-gen-prost", - "protoc-gen-tonic", - "rand 0.8.5", - "ring 0.16.20", - "ripemd", - "rust-embed", - "semver", - "serde 1.0.190", - "serde_json", - "serde_with 3.4.0", - "serde_yaml 0.9.27", - "sha2 0.10.8", - "sha3 0.10.8", - "spki 0.7.2", - "strum", - "thiserror", - "tokio", - "tokio-stream", - "tonic 0.9.2", - "tonic-health 0.9.2", - "tonic-reflection", - "tower-service", - "url 2.4.1", - "zerocopy 0.6.5", - "zeroize", -] - -[[package]] -name = "axum" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" -dependencies = [ - "async-trait", - "axum-core", - "base64 0.21.5", - "bitflags 1.3.2", - "bytes", - "futures-util", - "headers", - "http", - "http-body", - "hyper", - "itoa", - "matchit 0.7.3", - "memchr", - "mime", - "percent-encoding 2.3.0", - "pin-project-lite", - "rustversion", - "serde 1.0.190", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sha1", - "sync_wrapper", - "tokio", - "tokio-tungstenite", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "mime", - "rustversion", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-server" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447f28c85900215cc1bea282f32d4a2f22d55c5a300afdfbc661c8d6a632e063" -dependencies = [ - "arc-swap", - "bytes", - "futures-util", - "http", - "http-body", - "hyper", - "pin-project-lite", - "rustls 0.21.8", - "rustls-pemfile", - "tokio", - "tokio-rustls 0.24.1", - "tower-service", -] - -[[package]] -name = "backoff" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" -dependencies = [ - "futures-core", - "getrandom 0.2.10", - "instant", - "pin-project-lite", - "rand 0.8.5", - "tokio", -] - -[[package]] -name = "backtrace" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" - -[[package]] -name = "base64-url" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c5b0a88aa36e9f095ee2e2b13fb8c5e4313e022783aedacc123328c0084916d" -dependencies = [ - "base64 0.21.5", -] - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bcs" -version = "0.1.4" -source = "git+https://github.com/aptos-labs/bcs.git?rev=d31fab9d81748e2594be5cd5cdf845786a30562d#d31fab9d81748e2594be5cd5cdf845786a30562d" -dependencies = [ - "serde 1.0.190", - "thiserror", -] - -[[package]] -name = "bcs" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b6598a2f5d564fb7855dc6b06fd1c38cff5a72bd8b863a4d021938497b440a" -dependencies = [ - "serde 1.0.190", - "thiserror", -] - -[[package]] -name = "bech32" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" - -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" -dependencies = [ - "serde 1.0.190", -] - -[[package]] -name = "better_any" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b359aebd937c17c725e19efcb661200883f04c49c53e7132224dac26da39d4a0" -dependencies = [ - "better_typeid_derive", -] - -[[package]] -name = "better_typeid_derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3deeecb812ca5300b7d3f66f730cc2ebd3511c3d36c691dd79c165d5b19a26e3" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde 1.0.190", -] - -[[package]] -name = "bindgen" -version = "0.65.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" -dependencies = [ - "bitflags 1.3.2", - "cexpr", - "clang-sys", - "lazy_static 1.4.0", - "lazycell", - "peeking_take_while", - "prettyplease 0.2.15", - "proc-macro2 1.0.69", - "quote 1.0.33", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.39", -] - -[[package]] -name = "bip32" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30ed1d6f8437a487a266c8293aeb95b61a23261273e3e02912cdb8b68bf798b" -dependencies = [ - "bs58 0.4.0", - "hmac 0.12.1", - "k256 0.11.6", - "once_cell", - "pbkdf2", - "rand_core 0.6.4", - "ripemd", - "sha2 0.10.8", - "subtle", - "zeroize", -] - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitcoin-private" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" - -[[package]] -name = "bitcoin_hashes" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501" -dependencies = [ - "bitcoin-private", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" - -[[package]] -name = "bitmaps" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" -dependencies = [ - "typenum", -] - -[[package]] -name = "bitvec" -version = "0.20.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" -dependencies = [ - "funty 1.1.0", - "radium 0.6.2", - "tap", - "wyz 0.2.0", -] - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty 2.0.0", - "radium 0.7.0", - "tap", - "wyz 0.5.1", -] - -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "blake2-rfc" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -dependencies = [ - "arrayvec 0.4.12", - "constant_time_eq 0.1.5", -] - -[[package]] -name = "blake3" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" -dependencies = [ - "arrayref", - "arrayvec 0.7.4", - "cc", - "cfg-if", - "constant_time_eq 0.3.0", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "block-padding 0.2.1", - "generic-array", -] - -[[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 = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blst" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94087b935a822949d3291a9989ad2b2051ea141eda0fd4e478a75f6aa3e604b" -dependencies = [ - "cc", - "glob", - "threadpool", - "zeroize", -] - -[[package]] -name = "brotli" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "2.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" -dependencies = [ - "sha2 0.9.9", -] - -[[package]] -name = "bs58" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" -dependencies = [ - "sha2 0.10.8", - "tinyvec", -] - -[[package]] -name = "bstr" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" -dependencies = [ - "memchr", - "serde 1.0.190", -] - -[[package]] -name = "bulletproofs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40e698f1df446cc6246afd823afbe2d121134d089c9102c1dd26d1264991ba32" -dependencies = [ - "byteorder", - "clear_on_drop", - "curve25519-dalek-ng", - "digest 0.9.0", - "merlin", - "rand 0.8.5", - "rand_core 0.6.4", - "serde 1.0.190", - "serde_derive", - "sha3 0.9.1", - "subtle-ng", - "thiserror", -] - -[[package]] -name = "bumpalo" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" - -[[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.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" - -[[package]] -name = "bytemuck" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" -dependencies = [ - "serde 1.0.190", -] - -[[package]] -name = "bytes-varint" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c1820c7c366b9d26c47143e1604454105a59969aade54e4f695d96acc8332f" -dependencies = [ - "bytes", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "cached" -version = "0.43.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc2fafddf188d13788e7099295a59b99e99b2148ab2195cae454e754cc099925" -dependencies = [ - "async-trait", - "async_once", - "cached_proc_macro", - "cached_proc_macro_types", - "futures 0.3.28", - "hashbrown 0.13.2", - "instant", - "lazy_static 1.4.0", - "once_cell", - "thiserror", - "tokio", -] - -[[package]] -name = "cached_proc_macro" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10ca87c81aaa3a949dbbe2b5e6c2c45dbc94ba4897e45ea31ff9ec5087be3dc" -dependencies = [ - "cached_proc_macro_types", - "darling 0.14.4", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "cached_proc_macro_types" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" - -[[package]] -name = "camino" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" - -[[package]] -name = "canonical-block-executor" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-channel", - "avalanche-types", - "base64 0.21.5", - "bcs 0.1.6", - "bytes", - "chrono", - "ctor", - "derivative", - "dirs 5.0.1", - "env_logger", - "futures 0.3.28", - "hex", - "jsonrpc-core", - "jsonrpc-core-client", - "jsonrpc-derive", - "log", - "movement-sdk", - "rand 0.8.5", - "serde 1.0.190", - "serde_json", - "serde_with 2.3.3", - "sui-adapter-latest", - "sui-core", - "sui-swarm-config", - "sui-test-transaction-builder", - "sui-types", - "tempfile", - "tokio", - "tonic 0.8.3", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "canonical-move-natives" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-channel", - "avalanche-types", - "base64 0.21.5", - "bcs 0.1.6", - "bytes", - "chrono", - "derivative", - "dirs 5.0.1", - "env_logger", - "futures 0.3.28", - "hex", - "jsonrpc-core", - "jsonrpc-core-client", - "jsonrpc-derive", - "log", - "movement-sdk", - "rand 0.8.5", - "serde 1.0.190", - "serde_json", - "serde_with 2.3.3", - "sui-adapter-latest", - "sui-types", - "tokio", - "tonic 0.8.3", -] - -[[package]] -name = "canonical-move-resolver" -version = "0.1.0" -dependencies = [ - "anyhow", - "aptos-storage-interface", - "async-channel", - "avalanche-types", - "base64 0.21.5", - "bcs 0.1.6", - "bytes", - "chrono", - "derivative", - "dirs 5.0.1", - "env_logger", - "futures 0.3.28", - "hex", - "jsonrpc-core", - "jsonrpc-core-client", - "jsonrpc-derive", - "log", - "movement-sdk", - "rand 0.8.5", - "serde 1.0.190", - "serde_json", - "serde_with 2.3.3", - "sui-adapter-latest", - "sui-types", - "tokio", - "tonic 0.8.3", -] - -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "jobserver", - "libc", -] - -[[package]] -name = "cert-manager" -version = "0.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "166da6fda67aa44381158b8ad9c3b28d842951791c431ce61333e62cb9b05d5b" -dependencies = [ - "log", - "rand 0.8.5", - "random-manager", - "rcgen 0.10.0", - "rsa 0.9.3", - "rustls 0.21.8", - "rustls-pemfile", - "x509-parser 0.15.1", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom 7.1.3", -] - -[[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.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits 0.2.17", - "serde 1.0.190", - "wasm-bindgen", - "windows-targets 0.48.5", -] - -[[package]] -name = "chrono-tz" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e23185c0e21df6ed832a12e2bda87c7d1def6842881fb634a8511ced741b0d76" -dependencies = [ - "chrono", - "chrono-tz-build", - "phf", -] - -[[package]] -name = "chrono-tz-build" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" -dependencies = [ - "parse-zoneinfo", - "phf", - "phf_codegen", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "claims" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6995bbe186456c36307f8ea36be3eefe42f49d106896414e18efc4fb2f846b5" -dependencies = [ - "autocfg", -] - -[[package]] -name = "clang-sys" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim 0.8.0", - "textwrap 0.11.0", - "unicode-width", - "vec_map", -] - -[[package]] -name = "clap" -version = "4.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim 0.10.0", - "terminal_size", -] - -[[package]] -name = "clap_derive" -version = "4.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" -dependencies = [ - "heck 0.4.1", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "clap_lex" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" - -[[package]] -name = "clear_on_drop" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38508a63f4979f0048febc9966fadbd48e5dab31fd0ec6a3f151bbf4a74f7423" -dependencies = [ - "cc", -] - -[[package]] -name = "cmp-manager" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f5e2b424191b35b798b06e6c67fa5a5440a098925d931d7e91511d7d8fe275" - -[[package]] -name = "codespan" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3362992a0d9f1dd7c3d0e89e0ab2bb540b7a95fea8cd798090e758fda2899b5e" -dependencies = [ - "codespan-reporting", - "serde 1.0.190", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "serde 1.0.190", - "termcolor", - "unicode-width", -] - -[[package]] -name = "collectable" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08abddbaad209601e53c7dd4308d8c04c06f17bb7df006434e586a22b83be45a" - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - -[[package]] -name = "colored" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" -dependencies = [ - "is-terminal", - "lazy_static 1.4.0", - "windows-sys 0.48.0", -] - -[[package]] -name = "concurrent-queue" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "config" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369" -dependencies = [ - "lazy_static 1.4.0", - "nom 5.1.3", - "rust-ini", - "serde 1.0.190", - "serde-hjson", - "serde_json", - "toml", - "yaml-rust", -] - -[[package]] -name = "console" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" -dependencies = [ - "encode_unicode", - "lazy_static 1.4.0", - "libc", - "unicode-width", - "windows-sys 0.45.0", -] - -[[package]] -name = "const-oid" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" - -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] -name = "constant_time_eq" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - -[[package]] -name = "cpufeatures" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" -dependencies = [ - "cfg-if", - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossterm" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" -dependencies = [ - "bitflags 1.3.2", - "crossterm_winapi", - "libc", - "mio", - "parking_lot 0.12.1", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-bigint" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "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", - "rand_core 0.6.4", - "typenum", -] - -[[package]] -name = "crypto-mac" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "crypto-mac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "csv" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde 1.0.190", -] - -[[package]] -name = "csv-core" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" -dependencies = [ - "memchr", -] - -[[package]] -name = "ctor" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e366bff8cd32dd8754b0991fb66b279dc48f598c3a18914852a6673deef583" -dependencies = [ - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-ng" -version = "4.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.6.4", - "serde 1.0.190", - "subtle-ng", - "zeroize", -] - -[[package]] -name = "darling" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" -dependencies = [ - "darling_core 0.14.4", - "darling_macro 0.14.4", -] - -[[package]] -name = "darling" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" -dependencies = [ - "darling_core 0.20.3", - "darling_macro 0.20.3", -] - -[[package]] -name = "darling_core" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2 1.0.69", - "quote 1.0.33", - "strsim 0.10.0", - "syn 1.0.109", -] - -[[package]] -name = "darling_core" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2 1.0.69", - "quote 1.0.33", - "strsim 0.10.0", - "syn 2.0.39", -] - -[[package]] -name = "darling_macro" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" -dependencies = [ - "darling_core 0.14.4", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" -dependencies = [ - "darling_core 0.20.3", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.2", - "lock_api", - "once_cell", - "parking_lot_core 0.9.9", -] - -[[package]] -name = "data-encoding" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" - -[[package]] -name = "data-encoding-macro" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" -dependencies = [ - "data-encoding", - "data-encoding-macro-internal", -] - -[[package]] -name = "data-encoding-macro-internal" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" -dependencies = [ - "data-encoding", - "syn 1.0.109", -] - -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "pem-rfc7468 0.6.0", - "zeroize", -] - -[[package]] -name = "der" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" -dependencies = [ - "const-oid", - "pem-rfc7468 0.7.0", - "zeroize", -] - -[[package]] -name = "der-parser" -version = "8.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" -dependencies = [ - "asn1-rs", - "displaydoc", - "nom 7.1.3", - "num-bigint", - "num-traits 0.2.17", - "rusticata-macros", -] - -[[package]] -name = "deranged" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" -dependencies = [ - "powerfmt", - "serde 1.0.190", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "derive-syn-parse" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "derive_builder" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" -dependencies = [ - "darling 0.14.4", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "derive_builder_macro" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" -dependencies = [ - "derive_builder_core", - "syn 1.0.109", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case 0.4.0", - "proc-macro2 1.0.69", - "quote 1.0.33", - "rustc_version", - "syn 1.0.109", -] - -[[package]] -name = "deunicode" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a1abaf4d861455be59f64fd2b55606cb151fce304ede7165f410243ce96bde6" - -[[package]] -name = "difference" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" - -[[package]] -name = "difflib" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" - -[[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 0.10.4", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "directories" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" -dependencies = [ - "dirs-sys 0.3.7", -] - -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys 0.3.7", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys 0.4.1", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "displaydoc" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - -[[package]] -name = "downcast" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" - -[[package]] -name = "downcast-rs" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" - -[[package]] -name = "dyn-clone" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" - -[[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" -dependencies = [ - "der 0.6.1", - "elliptic-curve 0.12.3", - "rfc6979 0.3.1", - "signature 1.6.4", -] - -[[package]] -name = "ecdsa" -version = "0.16.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" -dependencies = [ - "der 0.7.8", - "digest 0.10.7", - "elliptic-curve 0.13.6", - "rfc6979 0.4.0", - "signature 2.1.0", - "spki 0.7.2", -] - -[[package]] -name = "ed25519" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" -dependencies = [ - "pkcs8 0.9.0", - "serde 1.0.190", - "signature 1.6.4", - "zeroize", -] - -[[package]] -name = "ed25519-consensus" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" -dependencies = [ - "curve25519-dalek-ng", - "hex", - "rand_core 0.6.4", - "serde 1.0.190", - "sha2 0.9.9", - "thiserror", - "zeroize", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand 0.7.3", - "serde 1.0.190", - "serde_bytes", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct 0.1.1", - "crypto-bigint 0.4.9", - "der 0.6.1", - "digest 0.10.7", - "ff 0.12.1", - "generic-array", - "group 0.12.1", - "rand_core 0.6.4", - "sec1 0.3.0", - "subtle", - "zeroize", -] - -[[package]] -name = "elliptic-curve" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" -dependencies = [ - "base16ct 0.2.0", - "crypto-bigint 0.5.3", - "digest 0.10.7", - "ff 0.13.0", - "generic-array", - "group 0.13.0", - "pem-rfc7468 0.7.0", - "pkcs8 0.10.2", - "rand_core 0.6.4", - "sec1 0.7.3", - "subtle", - "zeroize", -] - -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enum-compat-util" -version = "0.1.0-canonical-sui" -dependencies = [ - "serde_yaml 0.8.26", -] - -[[package]] -name = "enum_dispatch" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" -dependencies = [ - "once_cell", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "env_logger" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "erasable" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f11890ce181d47a64e5d1eb4b6caba0e7bae911a356723740d058a5d0340b7d" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "erased-serde" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" -dependencies = [ - "serde 1.0.190", -] - -[[package]] -name = "errno" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "ethabi" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" -dependencies = [ - "ethereum-types", - "hex", - "once_cell", - "regex", - "serde 1.0.190", - "serde_json", - "sha3 0.10.8", - "thiserror", - "uint", -] - -[[package]] -name = "ethbloom" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" -dependencies = [ - "crunchy", - "fixed-hash 0.8.0", - "impl-codec 0.6.0", - "impl-rlp", - "impl-serde 0.4.0", - "scale-info", - "tiny-keccak", -] - -[[package]] -name = "ethereum-types" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" -dependencies = [ - "ethbloom", - "fixed-hash 0.8.0", - "impl-codec 0.6.0", - "impl-rlp", - "impl-serde 0.4.0", - "primitive-types 0.12.2", - "scale-info", - "uint", -] - -[[package]] -name = "ethers-core" -version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6da5fa198af0d3be20c19192df2bd9590b92ce09a8421e793bec8851270f1b05" -dependencies = [ - "arrayvec 0.7.4", - "bytes", - "chrono", - "elliptic-curve 0.13.6", - "ethabi", - "generic-array", - "hex", - "k256 0.13.1", - "num_enum", - "open-fastrlp", - "rand 0.8.5", - "rlp", - "serde 1.0.190", - "serde_json", - "strum", - "tempfile", - "thiserror", - "tiny-keccak", - "unicode-xid 0.2.4", -] - -[[package]] -name = "ethnum" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "eyre" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" -dependencies = [ - "indenter", - "once_cell", -] - -[[package]] -name = "fail" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be3c61c59fdc91f5dbc3ea31ee8623122ce80057058be560654c5d410d181a6" -dependencies = [ - "lazy_static 1.4.0", - "log", - "rand 0.7.3", -] - -[[package]] -name = "fail" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe5e43d0f78a42ad591453aedb1d7ae631ce7ee445c7643691055a9ed8d3b01c" -dependencies = [ - "log", - "once_cell", - "rand 0.8.5", -] - -[[package]] -name = "fastcrypto" -version = "0.1.7" -source = "git+https://github.com/MystenLabs/fastcrypto?rev=517ec93ad2eba1807c9508f14953814a33523edc#517ec93ad2eba1807c9508f14953814a33523edc" -dependencies = [ - "aes", - "aes-gcm", - "ark-ec", - "ark-ff", - "ark-secp256r1", - "ark-serialize", - "auto_ops", - "base64ct", - "bincode", - "blake2", - "blake3", - "blst", - "bs58 0.4.0", - "bulletproofs", - "cbc", - "ctr", - "curve25519-dalek-ng", - "derive_more", - "digest 0.10.7", - "ecdsa 0.16.8", - "ed25519-consensus", - "elliptic-curve 0.13.6", - "eyre", - "fastcrypto-derive", - "generic-array", - "hex", - "hkdf 0.12.3", - "lazy_static 1.4.0", - "merlin", - "once_cell", - "p256", - "rand 0.8.5", - "readonly", - "rfc6979 0.4.0", - "rsa 0.8.2", - "schemars", - "secp256k1", - "serde 1.0.190", - "serde_bytes", - "serde_json", - "serde_with 2.3.3", - "sha2 0.10.8", - "sha3 0.10.8", - "signature 2.1.0", - "static_assertions", - "thiserror", - "tokio", - "typenum", - "zeroize", -] - -[[package]] -name = "fastcrypto-derive" -version = "0.1.3" -source = "git+https://github.com/MystenLabs/fastcrypto?rev=517ec93ad2eba1807c9508f14953814a33523edc#517ec93ad2eba1807c9508f14953814a33523edc" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "fastcrypto-tbls" -version = "0.1.0" -source = "git+https://github.com/MystenLabs/fastcrypto?rev=517ec93ad2eba1807c9508f14953814a33523edc#517ec93ad2eba1807c9508f14953814a33523edc" -dependencies = [ - "bcs 0.1.6", - "bincode", - "digest 0.10.7", - "fastcrypto", - "fastcrypto-derive", - "hex", - "itertools 0.10.5", - "rand 0.8.5", - "serde 1.0.190", - "sha3 0.10.8", - "tap", - "tracing", - "typenum", - "zeroize", -] - -[[package]] -name = "fastcrypto-zkp" -version = "0.1.2" -source = "git+https://github.com/MystenLabs/fastcrypto?rev=517ec93ad2eba1807c9508f14953814a33523edc#517ec93ad2eba1807c9508f14953814a33523edc" -dependencies = [ - "ark-bls12-381", - "ark-bn254", - "ark-ec", - "ark-ff", - "ark-groth16", - "ark-relations", - "ark-serialize", - "ark-snark", - "bcs 0.1.6", - "blst", - "byte-slice-cast", - "derive_more", - "fastcrypto", - "im", - "num-bigint", - "once_cell", - "poseidon-ark", - "regex", - "reqwest", - "rustls-webpki", - "schemars", - "serde 1.0.190", - "serde_json", -] - -[[package]] -name = "fastrand" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" - -[[package]] -name = "fdlimit" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4c9e43643f5a3be4ca5b67d26b98031ff9db6806c3440ae32e02e3ceac3f1b" -dependencies = [ - "libc", -] - -[[package]] -name = "ff" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fixed-hash" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" -dependencies = [ - "byteorder", - "rand 0.8.5", - "rustc-hex", - "static_assertions", -] - -[[package]] -name = "fixed-hash" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" -dependencies = [ - "byteorder", - "rand 0.8.5", - "rustc-hex", - "static_assertions", -] - -[[package]] -name = "fixedbitset" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" - -[[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.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" -dependencies = [ - "num-traits 0.2.17", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -dependencies = [ - "percent-encoding 2.3.0", -] - -[[package]] -name = "fragile" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" - -[[package]] -name = "funty" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" - -[[package]] -name = "futures-executor" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", - "num_cpus", -] - -[[package]] -name = "futures-io" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" - -[[package]] -name = "futures-macro" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "futures-sink" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" - -[[package]] -name = "futures-task" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" - -[[package]] -name = "futures-timer" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" - -[[package]] -name = "futures-util" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" -dependencies = [ - "futures 0.1.31", - "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 = [ - "serde 1.0.190", - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "gettid" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b20f40277dfb8a9dec7e2e945f6d8ff711e733c8f2a2c9b257562764b4c60d" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "ghash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "gimli" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" - -[[package]] -name = "git-version" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13ad01ffa8221f7fe8b936d6ffb2a3e7ad428885a04fad51866a5f33eafda57c" -dependencies = [ - "git-version-macro", -] - -[[package]] -name = "git-version-macro" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84488ccbdb24ad6f56dc1863b4a8154a7856cd3c6c7610401634fab3cb588dae" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "globset" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" -dependencies = [ - "aho-corasick", - "bstr", - "fnv", - "log", - "regex", -] - -[[package]] -name = "globwalk" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" -dependencies = [ - "bitflags 1.3.2", - "ignore", - "walkdir", -] - -[[package]] -name = "governor" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "821239e5672ff23e2a7060901fa622950bbd80b649cdaadd78d1c1767ed14eb4" -dependencies = [ - "cfg-if", - "dashmap", - "futures 0.3.28", - "futures-timer", - "no-std-compat", - "nonzero_ext", - "parking_lot 0.12.1", - "quanta", - "rand 0.8.5", - "smallvec", -] - -[[package]] -name = "group" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" -dependencies = [ - "ff 0.12.1", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff 0.13.0", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "h2" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap 1.9.3", - "slab", - "tokio", - "tokio-util 0.7.10", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.7", -] - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.6", -] - -[[package]] -name = "hashbrown" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" - -[[package]] -name = "hdrhistogram" -version = "7.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f19b9f54f7c7f55e31401bb647626ce0cf0f67b0004982ce815b3ee72a02aa8" -dependencies = [ - "base64 0.13.1", - "byteorder", - "crossbeam-channel", - "flate2", - "nom 7.1.3", - "num-traits 0.2.17", -] - -[[package]] -name = "headers" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" -dependencies = [ - "base64 0.21.5", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hkdf" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" -dependencies = [ - "digest 0.9.0", - "hmac 0.10.1", -] - -[[package]] -name = "hkdf" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" -dependencies = [ - "hmac 0.12.1", -] - -[[package]] -name = "hmac" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" -dependencies = [ - "crypto-mac 0.8.0", - "digest 0.9.0", -] - -[[package]] -name = "hmac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" -dependencies = [ - "crypto-mac 0.10.1", - "digest 0.9.0", -] - -[[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 = "hmac-drbg" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" -dependencies = [ - "digest 0.9.0", - "generic-array", - "hmac 0.8.1", -] - -[[package]] -name = "hmac-sha512" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e806677ce663d0a199541030c816847b36e8dc095f70dae4a4f4ad63da5383" - -[[package]] -name = "home" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", -] - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "http-range-header" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "humansize" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" -dependencies = [ - "libm", -] - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "hyper" -version = "0.14.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.4.10", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" -dependencies = [ - "http", - "hyper", - "log", - "rustls 0.20.9", - "rustls-native-certs", - "tokio", - "tokio-rustls 0.23.4", - "webpki-roots 0.22.6", -] - -[[package]] -name = "hyper-rustls" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" -dependencies = [ - "futures-util", - "http", - "hyper", - "log", - "rustls 0.21.8", - "rustls-native-certs", - "tokio", - "tokio-rustls 0.24.1", -] - -[[package]] -name = "hyper-timeout" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" -dependencies = [ - "hyper", - "pin-project-lite", - "tokio", - "tokio-io-timeout", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" -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 = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "ignore" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" -dependencies = [ - "globset", - "lazy_static 1.4.0", - "log", - "memchr", - "regex", - "same-file", - "thread_local", - "walkdir", - "winapi-util", -] - -[[package]] -name = "im" -version = "15.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" -dependencies = [ - "bitmaps", - "rand_core 0.6.4", - "rand_xoshiro", - "sized-chunks", - "typenum", - "version_check", -] - -[[package]] -name = "impl-codec" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" -dependencies = [ - "parity-scale-codec 2.3.1", -] - -[[package]] -name = "impl-codec" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" -dependencies = [ - "parity-scale-codec 3.6.5", -] - -[[package]] -name = "impl-rlp" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" -dependencies = [ - "rlp", -] - -[[package]] -name = "impl-serde" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" -dependencies = [ - "serde 1.0.190", -] - -[[package]] -name = "impl-serde" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" -dependencies = [ - "serde 1.0.190", -] - -[[package]] -name = "impl-trait-for-tuples" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "include_dir" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b56e147e6187d61e9d0f039f10e070d0c0a887e24fe0bb9ca3f29bfde62cab" -dependencies = [ - "glob", - "include_dir_impl", - "proc-macro-hack", -] - -[[package]] -name = "include_dir" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" -dependencies = [ - "glob", - "include_dir_macros", -] - -[[package]] -name = "include_dir_impl" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a0c890c85da4bab7bce4204c707396bbd3c6c8a681716a51c8814cfc2b682df" -dependencies = [ - "anyhow", - "proc-macro-hack", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "include_dir_macros" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", -] - -[[package]] -name = "indenter" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" - -[[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", - "serde 1.0.190", -] - -[[package]] -name = "indexmap" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" -dependencies = [ - "equivalent", - "hashbrown 0.14.2", - "serde 1.0.190", -] - -[[package]] -name = "indicatif" -version = "0.17.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" -dependencies = [ - "console", - "instant", - "number_prefix", - "portable-atomic", - "unicode-width", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "block-padding 0.3.3", - "generic-array", -] - -[[package]] -name = "insta" -version = "1.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d64600be34b2fcfc267740a243fa7744441bb4947a619ac4e5bb6507f35fbfc" -dependencies = [ - "console", - "lazy_static 1.4.0", - "linked-hash-map", - "pest", - "pest_derive", - "serde 1.0.190", - "similar", - "yaml-rust", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "integer-encoding" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" - -[[package]] -name = "internment" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ab388864246d58a276e60e7569a833d9cc4cd75c66e5ca77c177dad38e59996" -dependencies = [ - "ahash 0.7.7", - "dashmap", - "hashbrown 0.12.3", - "once_cell", - "parking_lot 0.12.1", -] - -[[package]] -name = "ipnet" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" - -[[package]] -name = "iri-string" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f0f7638c1e223529f1bfdc48c8b133b9e0b434094d1d28473161ee48b235f78" -dependencies = [ - "nom 7.1.3", -] - -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi 0.3.3", - "rustix", - "windows-sys 0.48.0", -] - -[[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.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "jobserver" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "json_to_table" -version = "0.6.0" -source = "git+https://github.com/zhiburt/tabled/?rev=e449317a1c02eb6b29e409ad6617e5d9eb7b3bd4#e449317a1c02eb6b29e409ad6617e5d9eb7b3bd4" -dependencies = [ - "serde_json", - "tabled", -] - -[[package]] -name = "jsonrpc-client-transports" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b99d4207e2a04fb4581746903c2bb7eb376f88de9c699d0f3e10feeac0cd3a" -dependencies = [ - "derive_more", - "futures 0.3.28", - "jsonrpc-core", - "jsonrpc-pubsub", - "log", - "serde 1.0.190", - "serde_json", - "url 1.7.2", -] - -[[package]] -name = "jsonrpc-core" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" -dependencies = [ - "futures 0.3.28", - "futures-executor", - "futures-util", - "log", - "serde 1.0.190", - "serde_derive", - "serde_json", -] - -[[package]] -name = "jsonrpc-core-client" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b51da17abecbdab3e3d4f26b01c5ec075e88d3abe3ab3b05dc9aa69392764ec0" -dependencies = [ - "futures 0.3.28", - "jsonrpc-client-transports", -] - -[[package]] -name = "jsonrpc-derive" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b939a78fa820cdfcb7ee7484466746a7377760970f6f9c6fe19f9edcc8a38d2" -dependencies = [ - "proc-macro-crate 0.1.5", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "jsonrpc-pubsub" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240f87695e6c6f62fb37f05c02c04953cf68d6408b8c1c89de85c7a0125b1011" -dependencies = [ - "futures 0.3.28", - "jsonrpc-core", - "lazy_static 1.4.0", - "log", - "parking_lot 0.11.2", - "rand 0.7.3", - "serde 1.0.190", -] - -[[package]] -name = "jsonrpsee" -version = "0.16.2" -source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" -dependencies = [ - "jsonrpsee-core", - "jsonrpsee-http-client", - "jsonrpsee-proc-macros", - "jsonrpsee-server", - "jsonrpsee-types", - "jsonrpsee-ws-client", - "tracing", -] - -[[package]] -name = "jsonrpsee-client-transport" -version = "0.16.2" -source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" -dependencies = [ - "futures-util", - "http", - "jsonrpsee-core", - "jsonrpsee-types", - "pin-project", - "rustls-native-certs", - "soketto", - "thiserror", - "tokio", - "tokio-rustls 0.23.4", - "tokio-util 0.7.10", - "tracing", - "webpki-roots 0.22.6", -] - -[[package]] -name = "jsonrpsee-core" -version = "0.16.2" -source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" -dependencies = [ - "anyhow", - "arrayvec 0.7.4", - "async-lock", - "async-trait", - "beef", - "futures-channel", - "futures-timer", - "futures-util", - "globset", - "hyper", - "jsonrpsee-types", - "parking_lot 0.12.1", - "rand 0.8.5", - "rustc-hash", - "serde 1.0.190", - "serde_json", - "soketto", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "jsonrpsee-http-client" -version = "0.16.2" -source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" -dependencies = [ - "async-trait", - "hyper", - "hyper-rustls 0.23.2", - "jsonrpsee-core", - "jsonrpsee-types", - "rustc-hash", - "serde 1.0.190", - "serde_json", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "jsonrpsee-proc-macros" -version = "0.16.2" -source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" -dependencies = [ - "heck 0.4.1", - "proc-macro-crate 1.1.3", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "jsonrpsee-server" -version = "0.16.2" -source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" -dependencies = [ - "futures-channel", - "futures-util", - "http", - "hyper", - "jsonrpsee-core", - "jsonrpsee-types", - "serde 1.0.190", - "serde_json", - "soketto", - "tokio", - "tokio-stream", - "tokio-util 0.7.10", - "tower", - "tracing", -] - -[[package]] -name = "jsonrpsee-types" -version = "0.16.2" -source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" -dependencies = [ - "anyhow", - "beef", - "serde 1.0.190", - "serde_json", - "thiserror", - "tracing", -] - -[[package]] -name = "jsonrpsee-ws-client" -version = "0.16.2" -source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" -dependencies = [ - "http", - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", -] - -[[package]] -name = "k256" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" -dependencies = [ - "cfg-if", - "ecdsa 0.14.8", - "elliptic-curve 0.12.3", - "sha2 0.10.8", - "sha3 0.10.8", -] - -[[package]] -name = "k256" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" -dependencies = [ - "cfg-if", - "ecdsa 0.16.8", - "elliptic-curve 0.13.6", - "once_cell", - "sha2 0.10.8", - "signature 2.1.0", -] - -[[package]] -name = "keccak" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "lazy_static" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin 0.5.2", -] - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "leb128" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" - -[[package]] -name = "lexical-core" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" -dependencies = [ - "arrayvec 0.5.2", - "bitflags 1.3.2", - "cfg-if", - "ryu", - "static_assertions", -] - -[[package]] -name = "libc" -version = "0.2.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" - -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" - -[[package]] -name = "libredox" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" -dependencies = [ - "bitflags 2.4.1", - "libc", - "redox_syscall 0.4.1", -] - -[[package]] -name = "librocksdb-sys" -version = "0.11.0+8.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" -dependencies = [ - "bindgen", - "bzip2-sys", - "cc", - "glob", - "libc", - "libz-sys", - "lz4-sys", - "zstd-sys", -] - -[[package]] -name = "libsecp256k1" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" -dependencies = [ - "arrayref", - "base64 0.13.1", - "digest 0.9.0", - "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.8.5", - "serde 1.0.190", - "sha2 0.9.9", - "typenum", -] - -[[package]] -name = "libsecp256k1-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", -] - -[[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libsecp256k1-gen-genmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libz-sys" -version = "1.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linux-raw-sys" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" - -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -dependencies = [ - "serde 1.0.190", -] - -[[package]] -name = "lru" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" -dependencies = [ - "hashbrown 0.13.2", -] - -[[package]] -name = "lz4-sys" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "mach2" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" -dependencies = [ - "libc", -] - -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - -[[package]] -name = "match_opt" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "405ba1524a1e6ae755334d6966380c60ec40157e0155f9032dd3c294b6384da9" - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - -[[package]] -name = "matchit" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - -[[package]] -name = "memchr" -version = "2.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" - -[[package]] -name = "memmap2" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "merlin" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" -dependencies = [ - "byteorder", - "keccak", - "rand_core 0.6.4", - "zeroize", -] - -[[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.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "minibytes" -version = "0.1.0" -source = "git+https://github.com/MystenLabs/mysticeti?rev=318d61d27f47d257d99a86983d835e9e9756bc59#318d61d27f47d257d99a86983d835e9e9756bc59" -dependencies = [ - "memmap2", - "serde 1.0.190", -] - -[[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.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", -] - -[[package]] -name = "mockall" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" -dependencies = [ - "cfg-if", - "downcast", - "fragile", - "lazy_static 1.4.0", - "mockall_derive", - "predicates", - "predicates-tree", -] - -[[package]] -name = "mockall_derive" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" -dependencies = [ - "cfg-if", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "more-asserts" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" - -[[package]] -name = "move-abigen" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "bcs 0.1.4", - "heck 0.3.3", - "log", - "move-binary-format 0.0.3-canonical-aptos", - "move-bytecode-verifier 0.1.0-canonical-aptos", - "move-command-line-common 0.1.0-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-model 0.1.0-canonical-aptos", - "serde 1.0.190", -] - -[[package]] -name = "move-abigen" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "bcs 0.1.6", - "heck 0.3.3", - "log", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-verifier 0.1.0-canonical-sui", - "move-command-line-common 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-model 0.1.0-canonical-sui", - "serde 1.0.190", -] - -[[package]] -name = "move-abstract-stack" -version = "0.0.1-canonical-sui" - -[[package]] -name = "move-binary-format" -version = "0.0.3-canonical-aptos" -dependencies = [ - "anyhow", - "backtrace", - "indexmap 1.9.3", - "move-core-types 0.0.4-canonical-aptos", - "once_cell", - "ref-cast", - "serde 1.0.190", - "variant_count", -] - -[[package]] -name = "move-binary-format" -version = "0.0.3-canonical-sui" -dependencies = [ - "anyhow", - "enum-compat-util", - "move-core-types 0.0.4-canonical-sui", - "move-proc-macros", - "ref-cast", - "serde 1.0.190", - "variant_count", -] - -[[package]] -name = "move-borrow-graph" -version = "0.0.1-canonical-aptos" - -[[package]] -name = "move-borrow-graph" -version = "0.0.1-canonical-sui" - -[[package]] -name = "move-bytecode-source-map" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "bcs 0.1.4", - "move-binary-format 0.0.3-canonical-aptos", - "move-command-line-common 0.1.0-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-ir-types 0.1.0-canonical-aptos", - "move-symbol-pool 0.1.0-canonical-aptos", - "serde 1.0.190", -] - -[[package]] -name = "move-bytecode-source-map" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "bcs 0.1.6", - "move-binary-format 0.0.3-canonical-sui", - "move-command-line-common 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-ir-types 0.1.0-canonical-sui", - "move-symbol-pool 0.1.0-canonical-sui", - "serde 1.0.190", -] - -[[package]] -name = "move-bytecode-utils" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "move-binary-format 0.0.3-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "petgraph 0.5.1", - "serde-reflection 0.3.6", -] - -[[package]] -name = "move-bytecode-utils" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "move-binary-format 0.0.3-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "petgraph 0.5.1", - "serde-reflection 0.3.6", -] - -[[package]] -name = "move-bytecode-verifier" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "fail 0.4.0", - "move-binary-format 0.0.3-canonical-aptos", - "move-borrow-graph 0.0.1-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "petgraph 0.5.1", - "typed-arena", -] - -[[package]] -name = "move-bytecode-verifier" -version = "0.1.0-canonical-sui" -dependencies = [ - "move-abstract-stack", - "move-binary-format 0.0.3-canonical-sui", - "move-borrow-graph 0.0.1-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-vm-config", - "petgraph 0.5.1", -] - -[[package]] -name = "move-bytecode-verifier-v0" -version = "0.1.0-canonical-sui" -dependencies = [ - "move-abstract-stack", - "move-binary-format 0.0.3-canonical-sui", - "move-borrow-graph 0.0.1-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-vm-config", - "petgraph 0.5.1", -] - -[[package]] -name = "move-bytecode-verifier-v1" -version = "0.1.0-canonical-sui" -dependencies = [ - "move-abstract-stack", - "move-binary-format 0.0.3-canonical-sui", - "move-borrow-graph 0.0.1-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-vm-config", - "petgraph 0.5.1", -] - -[[package]] -name = "move-command-line-common" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "difference", - "dirs-next", - "hex", - "move-core-types 0.0.4-canonical-aptos", - "num-bigint", - "once_cell", - "serde 1.0.190", - "sha2 0.9.9", - "walkdir", -] - -[[package]] -name = "move-command-line-common" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "difference", - "dirs-next", - "hex", - "move-core-types 0.0.4-canonical-sui", - "num-bigint", - "once_cell", - "serde 1.0.190", - "sha2 0.9.9", - "walkdir", -] - -[[package]] -name = "move-compiler" -version = "0.0.1-canonical-aptos" -dependencies = [ - "anyhow", - "bcs 0.1.4", - "clap 4.4.10", - "codespan-reporting", - "difference", - "hex", - "move-binary-format 0.0.3-canonical-aptos", - "move-borrow-graph 0.0.1-canonical-aptos", - "move-bytecode-source-map 0.1.0-canonical-aptos", - "move-bytecode-verifier 0.1.0-canonical-aptos", - "move-command-line-common 0.1.0-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-ir-to-bytecode 0.1.0-canonical-aptos", - "move-ir-types 0.1.0-canonical-aptos", - "move-symbol-pool 0.1.0-canonical-aptos", - "num-bigint", - "once_cell", - "petgraph 0.5.1", - "regex", - "sha3 0.9.1", - "tempfile", - "walkdir", -] - -[[package]] -name = "move-compiler" -version = "0.0.1-canonical-sui" -dependencies = [ - "anyhow", - "bcs 0.1.6", - "clap 4.4.10", - "codespan-reporting", - "hex", - "move-binary-format 0.0.3-canonical-sui", - "move-borrow-graph 0.0.1-canonical-sui", - "move-bytecode-source-map 0.1.0-canonical-sui", - "move-bytecode-verifier 0.1.0-canonical-sui", - "move-command-line-common 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-ir-to-bytecode 0.1.0-canonical-sui", - "move-ir-types 0.1.0-canonical-sui", - "move-symbol-pool 0.1.0-canonical-sui", - "once_cell", - "petgraph 0.5.1", - "regex", - "serde 1.0.190", - "tempfile", -] - -[[package]] -name = "move-core-types" -version = "0.0.4-canonical-aptos" -dependencies = [ - "anyhow", - "bcs 0.1.4", - "ethnum", - "hex", - "num", - "once_cell", - "primitive-types 0.10.1", - "rand 0.8.5", - "ref-cast", - "serde 1.0.190", - "serde_bytes", - "uint", -] - -[[package]] -name = "move-core-types" -version = "0.0.4-canonical-sui" -dependencies = [ - "anyhow", - "bcs 0.1.6", - "enum-compat-util", - "ethnum", - "hex", - "move-proc-macros", - "num", - "once_cell", - "primitive-types 0.10.1", - "rand 0.8.5", - "ref-cast", - "serde 1.0.190", - "serde_bytes", - "uint", -] - -[[package]] -name = "move-coverage" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "bcs 0.1.4", - "clap 4.4.10", - "codespan", - "colored", - "move-binary-format 0.0.3-canonical-aptos", - "move-bytecode-source-map 0.1.0-canonical-aptos", - "move-command-line-common 0.1.0-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-ir-types 0.1.0-canonical-aptos", - "once_cell", - "petgraph 0.5.1", - "serde 1.0.190", -] - -[[package]] -name = "move-coverage" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "bcs 0.1.6", - "clap 4.4.10", - "codespan", - "colored", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-source-map 0.1.0-canonical-sui", - "move-command-line-common 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-ir-types 0.1.0-canonical-sui", - "petgraph 0.5.1", - "serde 1.0.190", -] - -[[package]] -name = "move-disassembler" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "clap 4.4.10", - "colored", - "move-binary-format 0.0.3-canonical-aptos", - "move-bytecode-source-map 0.1.0-canonical-aptos", - "move-bytecode-verifier 0.1.0-canonical-aptos", - "move-command-line-common 0.1.0-canonical-aptos", - "move-compiler 0.0.1-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-coverage 0.1.0-canonical-aptos", - "move-ir-types 0.1.0-canonical-aptos", -] - -[[package]] -name = "move-disassembler" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "bcs 0.1.6", - "clap 4.4.10", - "colored", - "hex", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-source-map 0.1.0-canonical-sui", - "move-command-line-common 0.1.0-canonical-sui", - "move-compiler 0.0.1-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-coverage 0.1.0-canonical-sui", - "move-ir-types 0.1.0-canonical-sui", -] - -[[package]] -name = "move-docgen" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "codespan", - "codespan-reporting", - "itertools 0.10.5", - "log", - "move-compiler 0.0.1-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-model 0.1.0-canonical-aptos", - "num", - "once_cell", - "regex", - "serde 1.0.190", -] - -[[package]] -name = "move-docgen" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "codespan", - "codespan-reporting", - "itertools 0.10.5", - "log", - "move-compiler 0.0.1-canonical-sui", - "move-model 0.1.0-canonical-sui", - "num", - "once_cell", - "regex", - "serde 1.0.190", -] - -[[package]] -name = "move-errmapgen" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "bcs 0.1.4", - "log", - "move-command-line-common 0.1.0-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-model 0.1.0-canonical-aptos", - "serde 1.0.190", -] - -[[package]] -name = "move-errmapgen" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "move-command-line-common 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-model 0.1.0-canonical-sui", - "serde 1.0.190", -] - -[[package]] -name = "move-ir-to-bytecode" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "codespan-reporting", - "log", - "move-binary-format 0.0.3-canonical-aptos", - "move-bytecode-source-map 0.1.0-canonical-aptos", - "move-command-line-common 0.1.0-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-ir-to-bytecode-syntax 0.1.0-canonical-aptos", - "move-ir-types 0.1.0-canonical-aptos", - "move-symbol-pool 0.1.0-canonical-aptos", - "ouroboros 0.9.5", - "thiserror", -] - -[[package]] -name = "move-ir-to-bytecode" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "codespan-reporting", - "log", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-source-map 0.1.0-canonical-sui", - "move-command-line-common 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-ir-to-bytecode-syntax 0.1.0-canonical-sui", - "move-ir-types 0.1.0-canonical-sui", - "move-symbol-pool 0.1.0-canonical-sui", - "ouroboros 0.17.2", -] - -[[package]] -name = "move-ir-to-bytecode-syntax" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "hex", - "move-command-line-common 0.1.0-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-ir-types 0.1.0-canonical-aptos", - "move-symbol-pool 0.1.0-canonical-aptos", -] - -[[package]] -name = "move-ir-to-bytecode-syntax" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "hex", - "move-command-line-common 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-ir-types 0.1.0-canonical-sui", - "move-symbol-pool 0.1.0-canonical-sui", -] - -[[package]] -name = "move-ir-types" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "hex", - "move-command-line-common 0.1.0-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-symbol-pool 0.1.0-canonical-aptos", - "once_cell", - "serde 1.0.190", -] - -[[package]] -name = "move-ir-types" -version = "0.1.0-canonical-sui" -dependencies = [ - "hex", - "move-command-line-common 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-symbol-pool 0.1.0-canonical-sui", - "once_cell", - "serde 1.0.190", -] - -[[package]] -name = "move-model" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "codespan", - "codespan-reporting", - "internment", - "itertools 0.10.5", - "log", - "move-binary-format 0.0.3-canonical-aptos", - "move-bytecode-source-map 0.1.0-canonical-aptos", - "move-bytecode-verifier 0.1.0-canonical-aptos", - "move-command-line-common 0.1.0-canonical-aptos", - "move-compiler 0.0.1-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-disassembler 0.1.0-canonical-aptos", - "move-ir-types 0.1.0-canonical-aptos", - "move-symbol-pool 0.1.0-canonical-aptos", - "num", - "once_cell", - "regex", - "serde 1.0.190", - "trace", -] - -[[package]] -name = "move-model" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "codespan", - "codespan-reporting", - "internment", - "itertools 0.10.5", - "log", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-source-map 0.1.0-canonical-sui", - "move-command-line-common 0.1.0-canonical-sui", - "move-compiler 0.0.1-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-disassembler 0.1.0-canonical-sui", - "move-ir-types 0.1.0-canonical-sui", - "move-symbol-pool 0.1.0-canonical-sui", - "num", - "once_cell", - "regex", - "serde 1.0.190", -] - -[[package]] -name = "move-package" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "bcs 0.1.4", - "clap 4.4.10", - "colored", - "dirs-next", - "itertools 0.10.5", - "move-abigen 0.1.0-canonical-aptos", - "move-binary-format 0.0.3-canonical-aptos", - "move-bytecode-source-map 0.1.0-canonical-aptos", - "move-bytecode-utils 0.1.0-canonical-aptos", - "move-command-line-common 0.1.0-canonical-aptos", - "move-compiler 0.0.1-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-docgen 0.1.0-canonical-aptos", - "move-model 0.1.0-canonical-aptos", - "move-symbol-pool 0.1.0-canonical-aptos", - "named-lock", - "once_cell", - "petgraph 0.5.1", - "ptree", - "regex", - "reqwest", - "serde 1.0.190", - "serde_yaml 0.8.26", - "sha2 0.9.9", - "tempfile", - "toml", - "walkdir", - "whoami", -] - -[[package]] -name = "move-package" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "clap 4.4.10", - "colored", - "move-abigen 0.1.0-canonical-sui", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-source-map 0.1.0-canonical-sui", - "move-bytecode-utils 0.1.0-canonical-sui", - "move-command-line-common 0.1.0-canonical-sui", - "move-compiler 0.0.1-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-docgen 0.1.0-canonical-sui", - "move-model 0.1.0-canonical-sui", - "move-symbol-pool 0.1.0-canonical-sui", - "named-lock", - "once_cell", - "petgraph 0.5.1", - "regex", - "serde 1.0.190", - "serde_yaml 0.8.26", - "sha2 0.9.9", - "tempfile", - "toml", - "treeline", - "walkdir", - "whoami", -] - -[[package]] -name = "move-proc-macros" -version = "0.1.0-canonical-sui" -dependencies = [ - "enum-compat-util", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "move-prover" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "async-trait", - "atty", - "clap 4.4.10", - "codespan", - "codespan-reporting", - "futures 0.3.28", - "hex", - "itertools 0.10.5", - "log", - "move-abigen 0.1.0-canonical-aptos", - "move-binary-format 0.0.3-canonical-aptos", - "move-command-line-common 0.1.0-canonical-aptos", - "move-compiler 0.0.1-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-docgen 0.1.0-canonical-aptos", - "move-errmapgen 0.1.0-canonical-aptos", - "move-ir-types 0.1.0-canonical-aptos", - "move-model 0.1.0-canonical-aptos", - "move-prover-boogie-backend 0.1.0-canonical-aptos", - "move-stackless-bytecode 0.1.0-canonical-aptos", - "num", - "once_cell", - "pretty", - "rand 0.8.5", - "serde 1.0.190", - "serde_json", - "simplelog", - "tokio", - "toml", -] - -[[package]] -name = "move-prover" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "clap 4.4.10", - "codespan-reporting", - "itertools 0.10.5", - "log", - "move-abigen 0.1.0-canonical-sui", - "move-command-line-common 0.1.0-canonical-sui", - "move-compiler 0.0.1-canonical-sui", - "move-docgen 0.1.0-canonical-sui", - "move-errmapgen 0.1.0-canonical-sui", - "move-model 0.1.0-canonical-sui", - "move-prover-boogie-backend 0.1.0-canonical-sui", - "move-stackless-bytecode 0.1.0-canonical-sui", - "once_cell", - "serde 1.0.190", - "simplelog", - "toml", -] - -[[package]] -name = "move-prover-boogie-backend" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "async-trait", - "codespan", - "codespan-reporting", - "futures 0.3.28", - "itertools 0.10.5", - "log", - "move-binary-format 0.0.3-canonical-aptos", - "move-command-line-common 0.1.0-canonical-aptos", - "move-compiler 0.0.1-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-model 0.1.0-canonical-aptos", - "move-stackless-bytecode 0.1.0-canonical-aptos", - "num", - "once_cell", - "pretty", - "rand 0.8.5", - "regex", - "serde 1.0.190", - "serde_json", - "tera", - "tokio", -] - -[[package]] -name = "move-prover-boogie-backend" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "async-trait", - "codespan", - "codespan-reporting", - "futures 0.3.28", - "itertools 0.10.5", - "log", - "move-binary-format 0.0.3-canonical-sui", - "move-command-line-common 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-model 0.1.0-canonical-sui", - "move-stackless-bytecode 0.1.0-canonical-sui", - "num", - "once_cell", - "pretty", - "rand 0.8.5", - "regex", - "serde 1.0.190", - "tera", - "tokio", -] - -[[package]] -name = "move-read-write-set-types" -version = "0.0.3-canonical-sui" -dependencies = [ - "move-binary-format 0.0.3-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "serde 1.0.190", -] - -[[package]] -name = "move-stackless-bytecode" -version = "0.1.0-canonical-aptos" -dependencies = [ - "codespan", - "codespan-reporting", - "ethnum", - "im", - "itertools 0.10.5", - "log", - "move-binary-format 0.0.3-canonical-aptos", - "move-borrow-graph 0.0.1-canonical-aptos", - "move-bytecode-verifier 0.1.0-canonical-aptos", - "move-command-line-common 0.1.0-canonical-aptos", - "move-compiler 0.0.1-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-ir-to-bytecode 0.1.0-canonical-aptos", - "move-model 0.1.0-canonical-aptos", - "num", - "once_cell", - "paste", - "petgraph 0.5.1", - "serde 1.0.190", -] - -[[package]] -name = "move-stackless-bytecode" -version = "0.1.0-canonical-sui" -dependencies = [ - "codespan", - "codespan-reporting", - "ethnum", - "im", - "itertools 0.10.5", - "log", - "move-binary-format 0.0.3-canonical-sui", - "move-command-line-common 0.1.0-canonical-sui", - "move-compiler 0.0.1-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-model 0.1.0-canonical-sui", - "move-read-write-set-types", - "num", - "paste", - "petgraph 0.5.1", - "serde 1.0.190", -] - -[[package]] -name = "move-stdlib" -version = "0.1.1-canonical-sui" -dependencies = [ - "anyhow", - "hex", - "log", - "move-binary-format 0.0.3-canonical-sui", - "move-command-line-common 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-docgen 0.1.0-canonical-sui", - "move-errmapgen 0.1.0-canonical-sui", - "move-prover 0.1.0-canonical-sui", - "move-vm-runtime 0.1.0-canonical-sui", - "move-vm-types 0.1.0-canonical-sui", - "sha2 0.9.9", - "sha3 0.9.1", - "smallvec", - "walkdir", -] - -[[package]] -name = "move-stdlib-v0" -version = "0.1.1-canonical-sui" -dependencies = [ - "anyhow", - "hex", - "log", - "move-binary-format 0.0.3-canonical-sui", - "move-command-line-common 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-docgen 0.1.0-canonical-sui", - "move-errmapgen 0.1.0-canonical-sui", - "move-prover 0.1.0-canonical-sui", - "move-vm-runtime-v0", - "move-vm-types 0.1.0-canonical-sui", - "sha2 0.9.9", - "sha3 0.9.1", - "smallvec", - "walkdir", -] - -[[package]] -name = "move-stdlib-v1" -version = "0.1.1-canonical-sui" -dependencies = [ - "anyhow", - "hex", - "log", - "move-binary-format 0.0.3-canonical-sui", - "move-command-line-common 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-docgen 0.1.0-canonical-sui", - "move-errmapgen 0.1.0-canonical-sui", - "move-prover 0.1.0-canonical-sui", - "move-vm-runtime-v1", - "move-vm-types 0.1.0-canonical-sui", - "sha2 0.9.9", - "sha3 0.9.1", - "smallvec", - "walkdir", -] - -[[package]] -name = "move-symbol-pool" -version = "0.1.0-canonical-aptos" -dependencies = [ - "once_cell", - "serde 1.0.190", -] - -[[package]] -name = "move-symbol-pool" -version = "0.1.0-canonical-sui" -dependencies = [ - "once_cell", - "phf", - "serde 1.0.190", -] - -[[package]] -name = "move-table-extension" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "bcs 0.1.4", - "better_any", - "move-binary-format 0.0.3-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-vm-runtime 0.1.0-canonical-aptos", - "move-vm-types 0.1.0-canonical-aptos", - "once_cell", - "sha3 0.9.1", - "smallvec", -] - -[[package]] -name = "move-vm-config" -version = "0.1.0-canonical-sui" -dependencies = [ - "move-binary-format 0.0.3-canonical-sui", -] - -[[package]] -name = "move-vm-profiler" -version = "0.1.0-canonical-sui" -dependencies = [ - "move-vm-config", - "once_cell", - "serde 1.0.190", - "serde_json", -] - -[[package]] -name = "move-vm-runtime" -version = "0.1.0-canonical-aptos" -dependencies = [ - "better_any", - "fail 0.4.0", - "move-binary-format 0.0.3-canonical-aptos", - "move-bytecode-verifier 0.1.0-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-vm-types 0.1.0-canonical-aptos", - "once_cell", - "parking_lot 0.11.2", - "sha3 0.9.1", - "tracing", -] - -[[package]] -name = "move-vm-runtime" -version = "0.1.0-canonical-sui" -dependencies = [ - "better_any", - "fail 0.4.0", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-verifier 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-vm-config", - "move-vm-profiler", - "move-vm-types 0.1.0-canonical-sui", - "once_cell", - "parking_lot 0.11.2", - "sha3 0.9.1", - "smallvec", - "tracing", -] - -[[package]] -name = "move-vm-runtime-v0" -version = "0.1.0-canonical-sui" -dependencies = [ - "better_any", - "fail 0.4.0", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-verifier-v0", - "move-core-types 0.0.4-canonical-sui", - "move-vm-config", - "move-vm-profiler", - "move-vm-types 0.1.0-canonical-sui", - "once_cell", - "parking_lot 0.11.2", - "sha3 0.9.1", - "smallvec", - "tracing", -] - -[[package]] -name = "move-vm-runtime-v1" -version = "0.1.0-canonical-sui" -dependencies = [ - "better_any", - "fail 0.4.0", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-verifier-v1", - "move-core-types 0.0.4-canonical-sui", - "move-vm-config", - "move-vm-profiler", - "move-vm-types 0.1.0-canonical-sui", - "once_cell", - "parking_lot 0.11.2", - "sha3 0.9.1", - "smallvec", - "tracing", -] - -[[package]] -name = "move-vm-test-utils" -version = "0.1.0-canonical-aptos" -dependencies = [ - "anyhow", - "move-binary-format 0.0.3-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "move-table-extension", - "move-vm-types 0.1.0-canonical-aptos", - "once_cell", - "serde 1.0.190", -] - -[[package]] -name = "move-vm-test-utils" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "move-binary-format 0.0.3-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-vm-profiler", - "move-vm-types 0.1.0-canonical-sui", - "once_cell", - "serde 1.0.190", -] - -[[package]] -name = "move-vm-types" -version = "0.1.0-canonical-aptos" -dependencies = [ - "bcs 0.1.4", - "move-binary-format 0.0.3-canonical-aptos", - "move-core-types 0.0.4-canonical-aptos", - "once_cell", - "serde 1.0.190", - "smallvec", -] - -[[package]] -name = "move-vm-types" -version = "0.1.0-canonical-sui" -dependencies = [ - "bcs 0.1.6", - "move-binary-format 0.0.3-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-vm-profiler", - "serde 1.0.190", - "smallvec", -] - -[[package]] -name = "movement-sdk" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "serde 1.0.190", -] - -[[package]] -name = "msim" -version = "0.1.0" -source = "git+https://github.com/MystenLabs/mysten-sim.git?rev=1a52783d6600ecc22e15253a982f77881bd47c77#1a52783d6600ecc22e15253a982f77881bd47c77" -dependencies = [ - "ahash 0.7.7", - "async-task", - "bincode", - "bytes", - "cc", - "downcast-rs", - "erasable", - "futures 0.3.28", - "lazy_static 1.4.0", - "libc", - "msim-macros", - "naive-timer", - "pin-project-lite", - "rand 0.8.5", - "real_tokio", - "serde 1.0.190", - "socket2 0.4.10", - "tap", - "tokio-util 0.7.7", - "toml", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "msim-macros" -version = "0.1.0" -source = "git+https://github.com/MystenLabs/mysten-sim.git?rev=1a52783d6600ecc22e15253a982f77881bd47c77#1a52783d6600ecc22e15253a982f77881bd47c77" -dependencies = [ - "darling 0.14.4", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "multiaddr" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b36f567c7099511fa8612bbbb52dda2419ce0bdbacf31714e3a5ffdb766d3bd" -dependencies = [ - "arrayref", - "byteorder", - "data-encoding", - "log", - "multibase", - "multihash", - "percent-encoding 2.3.0", - "serde 1.0.190", - "static_assertions", - "unsigned-varint", - "url 2.4.1", -] - -[[package]] -name = "multibase" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" -dependencies = [ - "base-x", - "data-encoding", - "data-encoding-macro", -] - -[[package]] -name = "multihash" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" -dependencies = [ - "core2", - "multihash-derive", - "unsigned-varint", -] - -[[package]] -name = "multihash-derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" -dependencies = [ - "proc-macro-crate 1.1.3", - "proc-macro-error", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", - "synstructure", -] - -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "mysten-common" -version = "0.1.0-canonical-sui" -dependencies = [ - "futures 0.3.28", - "parking_lot 0.12.1", - "tokio", - "workspace-hack", -] - -[[package]] -name = "mysten-metrics" -version = "0.7.0-canonical-sui" -dependencies = [ - "async-trait", - "axum", - "dashmap", - "futures 0.3.28", - "once_cell", - "parking_lot 0.12.1", - "prometheus", - "prometheus-closure-metric", - "scopeguard", - "tap", - "tokio", - "tracing", - "uuid", - "workspace-hack", -] - -[[package]] -name = "mysten-network" -version = "0.2.0-canonical-sui" -dependencies = [ - "anemo", - "bcs 0.1.6", - "bytes", - "eyre", - "futures 0.3.28", - "http", - "multiaddr", - "serde 1.0.190", - "snap", - "tokio", - "tokio-stream", - "tonic 0.10.2", - "tonic-health 0.10.2", - "tower", - "tower-http", - "tracing", - "workspace-hack", -] - -[[package]] -name = "mysten-util-mem" -version = "0.11.0-canonical-sui" -dependencies = [ - "cfg-if", - "ed25519-consensus", - "fastcrypto", - "fastcrypto-tbls", - "hashbrown 0.12.3", - "impl-trait-for-tuples", - "indexmap 1.9.3", - "mysten-util-mem-derive", - "once_cell", - "parking_lot 0.12.1", - "roaring", - "smallvec", - "workspace-hack", -] - -[[package]] -name = "mysten-util-mem-derive" -version = "0.1.0-canonical-sui" -dependencies = [ - "proc-macro2 1.0.69", - "syn 1.0.109", - "synstructure", - "workspace-hack", -] - -[[package]] -name = "mysticeti-core" -version = "0.1.0" -source = "git+https://github.com/MystenLabs/mysticeti?rev=318d61d27f47d257d99a86983d835e9e9756bc59#318d61d27f47d257d99a86983d835e9e9756bc59" -dependencies = [ - "async-trait", - "axum", - "bincode", - "blake2", - "crc32fast", - "digest 0.10.7", - "ed25519-consensus", - "eyre", - "futures 0.3.28", - "gettid", - "hex", - "hyper", - "libc", - "memmap2", - "minibytes", - "parking_lot 0.12.1", - "prometheus", - "rand 0.8.5", - "serde 1.0.190", - "serde_yaml 0.9.27", - "tabled", - "tempfile", - "tokio", - "tracing", - "tracing-core", - "tracing-subscriber", - "zeroize", -] - -[[package]] -name = "naive-timer" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034a0ad7deebf0c2abcf2435950a6666c3c15ea9d8fad0c0f48efa8a7f843fed" - -[[package]] -name = "named-lock" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a3eb6b7c682b65d1f631ec3176829d72ab450b3aacdd3f719bf220822e59ac" -dependencies = [ - "libc", - "once_cell", - "parking_lot 0.12.1", - "thiserror", - "widestring", - "winapi", -] - -[[package]] -name = "narwhal-config" -version = "0.1.0-canonical-sui" -dependencies = [ - "fastcrypto", - "fastcrypto-tbls", - "match_opt", - "mysten-network", - "mysten-util-mem", - "narwhal-crypto", - "rand 0.8.5", - "serde 1.0.190", - "serde_json", - "thiserror", - "tracing", - "workspace-hack", -] - -[[package]] -name = "narwhal-crypto" -version = "0.1.0-canonical-sui" -dependencies = [ - "bcs 0.1.6", - "fastcrypto", - "fastcrypto-tbls", - "serde 1.0.190", - "shared-crypto", - "workspace-hack", -] - -[[package]] -name = "narwhal-executor" -version = "0.1.0-canonical-sui" -dependencies = [ - "async-trait", - "bcs 0.1.6", - "bincode", - "bytes", - "fastcrypto", - "futures 0.3.28", - "mockall", - "mysten-metrics", - "narwhal-config", - "narwhal-crypto", - "narwhal-network", - "narwhal-primary", - "narwhal-storage", - "narwhal-types", - "prometheus", - "serde 1.0.190", - "sui-protocol-config", - "thiserror", - "tokio", - "tonic 0.10.2", - "tracing", - "typed-store", - "workspace-hack", -] - -[[package]] -name = "narwhal-network" -version = "0.1.0-canonical-sui" -dependencies = [ - "anemo", - "anemo-tower", - "anyhow", - "async-trait", - "axum", - "axum-server", - "backoff", - "bytes", - "dashmap", - "futures 0.3.28", - "mysten-common", - "mysten-metrics", - "narwhal-crypto", - "narwhal-types", - "parking_lot 0.12.1", - "prometheus", - "quinn-proto", - "rand 0.8.5", - "sui-macros", - "tokio", - "tower", - "tracing", - "workspace-hack", -] - -[[package]] -name = "narwhal-node" -version = "0.1.0-canonical-sui" -dependencies = [ - "anemo", - "arc-swap", - "async-trait", - "axum", - "bytes", - "cfg-if", - "clap 4.4.10", - "eyre", - "fastcrypto", - "futures 0.3.28", - "mysten-metrics", - "mysten-network", - "narwhal-config", - "narwhal-crypto", - "narwhal-executor", - "narwhal-network", - "narwhal-primary", - "narwhal-storage", - "narwhal-types", - "narwhal-worker", - "prometheus", - "rand 0.8.5", - "reqwest", - "sui-keys", - "sui-protocol-config", - "sui-types", - "telemetry-subscribers", - "thiserror", - "tokio", - "tokio-stream", - "tracing", - "tracing-subscriber", - "url 2.4.1", - "workspace-hack", -] - -[[package]] -name = "narwhal-primary" -version = "0.1.0-canonical-sui" -dependencies = [ - "anemo", - "anemo-tower", - "anyhow", - "async-trait", - "backoff", - "bcs 0.1.6", - "bytes", - "cfg-if", - "fastcrypto", - "fastcrypto-tbls", - "futures 0.3.28", - "governor", - "itertools 0.10.5", - "mysten-common", - "mysten-metrics", - "mysten-network", - "narwhal-config", - "narwhal-crypto", - "narwhal-network", - "narwhal-storage", - "narwhal-types", - "once_cell", - "parking_lot 0.12.1", - "prometheus", - "rand 0.8.5", - "sui-macros", - "sui-protocol-config", - "tap", - "thiserror", - "tokio", - "tokio-stream", - "tower", - "tracing", - "typed-store", - "workspace-hack", -] - -[[package]] -name = "narwhal-storage" -version = "0.1.0-canonical-sui" -dependencies = [ - "fastcrypto", - "futures 0.3.28", - "lru", - "mysten-common", - "narwhal-config", - "narwhal-types", - "parking_lot 0.12.1", - "prometheus", - "sui-macros", - "tap", - "tempfile", - "tokio", - "tracing", - "typed-store", - "workspace-hack", -] - -[[package]] -name = "narwhal-test-utils" -version = "0.1.0-canonical-sui" -dependencies = [ - "anemo", - "fastcrypto", - "fdlimit", - "indexmap 1.9.3", - "itertools 0.10.5", - "mysten-metrics", - "mysten-network", - "narwhal-config", - "narwhal-crypto", - "narwhal-executor", - "narwhal-network", - "narwhal-node", - "narwhal-primary", - "narwhal-storage", - "narwhal-types", - "narwhal-worker", - "once_cell", - "prometheus", - "rand 0.8.5", - "sui-protocol-config", - "telemetry-subscribers", - "tempfile", - "tokio", - "tonic 0.10.2", - "tracing", - "typed-store", - "workspace-hack", -] - -[[package]] -name = "narwhal-types" -version = "0.1.0-canonical-sui" -dependencies = [ - "anemo", - "anemo-build", - "anyhow", - "base64 0.21.5", - "bcs 0.1.6", - "bytes", - "derive_builder", - "enum_dispatch", - "fastcrypto", - "fastcrypto-tbls", - "futures 0.3.28", - "indexmap 1.9.3", - "mockall", - "mysten-common", - "mysten-metrics", - "mysten-network", - "mysten-util-mem", - "narwhal-config", - "narwhal-crypto", - "once_cell", - "prometheus", - "proptest", - "proptest-derive", - "prost 0.12.1", - "prost-build 0.12.1", - "protobuf-src", - "rand 0.8.5", - "roaring", - "rustversion", - "serde 1.0.190", - "serde_with 2.3.3", - "sui-protocol-config", - "thiserror", - "tokio", - "tonic 0.10.2", - "tonic-build 0.10.2", - "tracing", - "typed-store", - "workspace-hack", -] - -[[package]] -name = "narwhal-worker" -version = "0.1.0-canonical-sui" -dependencies = [ - "anemo", - "anemo-tower", - "anyhow", - "arc-swap", - "async-trait", - "byteorder", - "bytes", - "eyre", - "fastcrypto", - "futures 0.3.28", - "governor", - "itertools 0.10.5", - "mysten-metrics", - "mysten-network", - "narwhal-config", - "narwhal-crypto", - "narwhal-network", - "narwhal-types", - "prometheus", - "rand 0.8.5", - "sui-protocol-config", - "tap", - "thiserror", - "tokio", - "tonic 0.10.2", - "tower", - "tracing", - "typed-store", - "workspace-hack", -] - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static 1.4.0", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "no-std-compat" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "nom" -version = "5.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" -dependencies = [ - "lexical-core", - "memchr", - "version_check", -] - -[[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 = "nonzero_ext" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" - -[[package]] -name = "normalize-line-endings" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits 0.2.17", -] - -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits 0.2.17", - "rand 0.8.5", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static 1.4.0", - "libm", - "num-integer", - "num-iter", - "num-traits 0.2.17", - "rand 0.8.5", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-complex" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" -dependencies = [ - "num-traits 0.2.17", -] - -[[package]] -name = "num-derive" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits 0.2.17", -] - -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg", - "num-integer", - "num-traits 0.2.17", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits 0.2.17", -] - -[[package]] -name = "num-traits" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -dependencies = [ - "num-traits 0.2.17", -] - -[[package]] -name = "num-traits" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.3", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" -dependencies = [ - "proc-macro-crate 1.1.3", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - -[[package]] -name = "object" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" -dependencies = [ - "memchr", -] - -[[package]] -name = "object_store" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f930c88a43b1c3f6e776dfe495b4afab89882dbc81530c632db2ed65451ebcb4" -dependencies = [ - "async-trait", - "base64 0.21.5", - "bytes", - "chrono", - "futures 0.3.28", - "humantime", - "hyper", - "itertools 0.11.0", - "parking_lot 0.12.1", - "percent-encoding 2.3.0", - "quick-xml", - "rand 0.8.5", - "reqwest", - "ring 0.16.20", - "rustls-pemfile", - "serde 1.0.190", - "serde_json", - "snafu", - "tokio", - "tracing", - "url 2.4.1", - "walkdir", -] - -[[package]] -name = "oid-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" -dependencies = [ - "asn1-rs", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "open-fastrlp" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" -dependencies = [ - "arrayvec 0.7.4", - "auto_impl", - "bytes", - "ethereum-types", - "open-fastrlp-derive", -] - -[[package]] -name = "open-fastrlp-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" -dependencies = [ - "bytes", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "openssl" -version = "0.10.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33" -dependencies = [ - "bitflags 2.4.1", - "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 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "opentelemetry" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9591d937bc0e6d2feb6f71a559540ab300ea49955229c347a517a28d27784c54" -dependencies = [ - "opentelemetry_api", - "opentelemetry_sdk", -] - -[[package]] -name = "opentelemetry-otlp" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5e5a5c4135864099f3faafbe939eb4d7f9b80ebf68a8448da961b32a7c1275" -dependencies = [ - "async-trait", - "futures-core", - "http", - "opentelemetry-proto", - "opentelemetry-semantic-conventions", - "opentelemetry_api", - "opentelemetry_sdk", - "prost 0.11.9", - "thiserror", - "tokio", - "tonic 0.9.2", -] - -[[package]] -name = "opentelemetry-proto" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e3f814aa9f8c905d0ee4bde026afd3b2577a97c10e1699912e3e44f0c4cbeb" -dependencies = [ - "opentelemetry_api", - "opentelemetry_sdk", - "prost 0.11.9", - "tonic 0.9.2", -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73c9f9340ad135068800e7f1b24e9e09ed9e7143f5bf8518ded3d3ec69789269" -dependencies = [ - "opentelemetry", -] - -[[package]] -name = "opentelemetry_api" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a81f725323db1b1206ca3da8bb19874bbd3f57c3bcd59471bfb04525b265b9b" -dependencies = [ - "futures-channel", - "futures-util", - "indexmap 1.9.3", - "js-sys", - "once_cell", - "pin-project-lite", - "thiserror", - "urlencoding", -] - -[[package]] -name = "opentelemetry_sdk" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa8e705a0612d48139799fcbaba0d4a90f06277153e43dd2bdc16c6f0edd8026" -dependencies = [ - "async-trait", - "crossbeam-channel", - "futures-channel", - "futures-executor", - "futures-util", - "once_cell", - "opentelemetry_api", - "ordered-float 3.9.2", - "percent-encoding 2.3.0", - "rand 0.8.5", - "regex", - "serde_json", - "thiserror", - "tokio", - "tokio-stream", -] - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits 0.2.17", -] - -[[package]] -name = "ordered-float" -version = "3.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" -dependencies = [ - "num-traits 0.2.17", -] - -[[package]] -name = "ouroboros" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeff60e3e37407a80ead3e9458145b456e978c4068cddbfea6afb48572962ca" -dependencies = [ - "ouroboros_macro 0.9.5", - "stable_deref_trait", -] - -[[package]] -name = "ouroboros" -version = "0.15.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" -dependencies = [ - "aliasable", - "ouroboros_macro 0.15.6", -] - -[[package]] -name = "ouroboros" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2ba07320d39dfea882faa70554b4bd342a5f273ed59ba7c1c6b4c840492c954" -dependencies = [ - "aliasable", - "ouroboros_macro 0.17.2", - "static_assertions", -] - -[[package]] -name = "ouroboros_macro" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03f2cb802b5bdfdf52f1ffa0b54ce105e4d346e91990dd571f86c91321ad49e2" -dependencies = [ - "Inflector", - "proc-macro-error", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "ouroboros_macro" -version = "0.15.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" -dependencies = [ - "Inflector", - "proc-macro-error", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "ouroboros_macro" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8" -dependencies = [ - "heck 0.4.1", - "proc-macro-error", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa 0.16.8", - "elliptic-curve 0.13.6", - "primeorder", - "sha2 0.10.8", -] - -[[package]] -name = "papergrid" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae7891b22598926e4398790c8fe6447930c72a67d36d983a49d6ce682ce83290" -dependencies = [ - "bytecount", - "fnv", - "unicode-width", -] - -[[package]] -name = "parity-scale-codec" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" -dependencies = [ - "arrayvec 0.7.4", - "bitvec 0.20.4", - "byte-slice-cast", - "impl-trait-for-tuples", - "parity-scale-codec-derive 2.3.1", - "serde 1.0.190", -] - -[[package]] -name = "parity-scale-codec" -version = "3.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" -dependencies = [ - "arrayvec 0.7.4", - "bitvec 1.0.1", - "byte-slice-cast", - "impl-trait-for-tuples", - "parity-scale-codec-derive 3.6.5", - "serde 1.0.190", -] - -[[package]] -name = "parity-scale-codec-derive" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" -dependencies = [ - "proc-macro-crate 1.1.3", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "parity-scale-codec-derive" -version = "3.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" -dependencies = [ - "proc-macro-crate 1.1.3", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core 0.9.9", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.4.1", - "smallvec", - "windows-targets 0.48.5", -] - -[[package]] -name = "parse-zoneinfo" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" -dependencies = [ - "regex", -] - -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - -[[package]] -name = "pem" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" -dependencies = [ - "base64 0.13.1", -] - -[[package]] -name = "pem-rfc7468" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" -dependencies = [ - "base64ct", -] - -[[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 = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - -[[package]] -name = "pest" -version = "2.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" -dependencies = [ - "memchr", - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "pest_meta" -version = "2.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" -dependencies = [ - "once_cell", - "pest", - "sha2 0.10.8", -] - -[[package]] -name = "petgraph" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" -dependencies = [ - "fixedbitset 0.2.0", - "indexmap 1.9.3", -] - -[[package]] -name = "petgraph" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" -dependencies = [ - "fixedbitset 0.4.2", - "indexmap 2.1.0", -] - -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_macros", - "phf_shared", -] - -[[package]] -name = "phf_codegen" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs1" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff33bdbdfc54cc98a2eca766ebdec3e1b8fb7387523d5c9c9a2891da856f719" -dependencies = [ - "der 0.6.1", - "pkcs8 0.9.0", - "spki 0.6.0", - "zeroize", -] - -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der 0.7.8", - "pkcs8 0.10.2", - "spki 0.7.2", -] - -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der 0.6.1", - "spki 0.6.0", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der 0.7.8", - "spki 0.7.2", -] - -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "polyval" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "portable-atomic" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bccab0e7fd7cc19f820a1c8c91720af652d0c88dc9664dd72aef2614f04af3b" - -[[package]] -name = "poseidon-ark" -version = "0.0.1" -source = "git+https://github.com/arnaucube/poseidon-ark.git?rev=ff7f5e05d55667b4ffba129b837da780c4c5c849#ff7f5e05d55667b4ffba129b837da780c4c5c849" -dependencies = [ - "ark-bn254", - "ark-ff", - "ark-std", -] - -[[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.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "predicates" -version = "2.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" -dependencies = [ - "difflib", - "float-cmp", - "itertools 0.10.5", - "normalize-line-endings", - "predicates-core", - "regex", -] - -[[package]] -name = "predicates-core" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" - -[[package]] -name = "predicates-tree" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" -dependencies = [ - "predicates-core", - "termtree", -] - -[[package]] -name = "prefix-manager" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21965b29633f6d9b2ebc05ba4c348173389e38da66de71dcd0b2bd8b9cde713c" - -[[package]] -name = "pretty" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9940b913ee56ddd94aec2d3cd179dd47068236f42a1a6415ccf9d880ce2a61" -dependencies = [ - "arrayvec 0.5.2", - "typed-arena", -] - -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2 1.0.69", - "syn 1.0.109", -] - -[[package]] -name = "prettyplease" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" -dependencies = [ - "proc-macro2 1.0.69", - "syn 2.0.39", -] - -[[package]] -name = "primeorder" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7dbe9ed3b56368bd99483eb32fe9c17fdd3730aebadc906918ce78d54c7eeb4" -dependencies = [ - "elliptic-curve 0.13.6", -] - -[[package]] -name = "primitive-types" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" -dependencies = [ - "fixed-hash 0.7.0", - "impl-codec 0.5.1", - "impl-serde 0.3.2", - "uint", -] - -[[package]] -name = "primitive-types" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" -dependencies = [ - "fixed-hash 0.8.0", - "impl-codec 0.6.0", - "impl-rlp", - "impl-serde 0.4.0", - "scale-info", - "uint", -] - -[[package]] -name = "proc-macro-crate" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" -dependencies = [ - "toml", -] - -[[package]] -name = "proc-macro-crate" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" -dependencies = [ - "thiserror", - "toml", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid 0.1.0", -] - -[[package]] -name = "proc-macro2" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prometheus" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" -dependencies = [ - "cfg-if", - "fnv", - "lazy_static 1.4.0", - "memchr", - "parking_lot 0.12.1", - "protobuf", - "thiserror", -] - -[[package]] -name = "prometheus-closure-metric" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "prometheus", - "protobuf", - "workspace-hack", -] - -[[package]] -name = "proptest" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e" -dependencies = [ - "bit-set", - "bit-vec", - "bitflags 2.4.1", - "lazy_static 1.4.0", - "num-traits 0.2.17", - "rand 0.8.5", - "rand_chacha 0.3.1", - "rand_xorshift", - "regex-syntax 0.7.5", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "proptest-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90b46295382dc76166cb7cf2bb4a97952464e4b7ed5a43e6cd34e1fec3349ddc" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", -] - -[[package]] -name = "prost" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" -dependencies = [ - "bytes", - "prost-derive 0.11.9", -] - -[[package]] -name = "prost" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fdd22f3b9c31b53c060df4a0613a1c7f062d4115a2b984dd15b1858f7e340d" -dependencies = [ - "bytes", - "prost-derive 0.12.1", -] - -[[package]] -name = "prost-build" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" -dependencies = [ - "bytes", - "heck 0.4.1", - "itertools 0.10.5", - "lazy_static 1.4.0", - "log", - "multimap", - "petgraph 0.6.4", - "prettyplease 0.1.25", - "prost 0.11.9", - "prost-types 0.11.9", - "regex", - "syn 1.0.109", - "tempfile", - "which", -] - -[[package]] -name = "prost-build" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac" -dependencies = [ - "bytes", - "heck 0.4.1", - "itertools 0.11.0", - "log", - "multimap", - "once_cell", - "petgraph 0.6.4", - "prettyplease 0.2.15", - "prost 0.12.1", - "prost-types 0.12.1", - "regex", - "syn 2.0.39", - "tempfile", - "which", -] - -[[package]] -name = "prost-derive" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" -dependencies = [ - "anyhow", - "itertools 0.10.5", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "prost-derive" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" -dependencies = [ - "anyhow", - "itertools 0.11.0", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "prost-types" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" -dependencies = [ - "prost 0.11.9", -] - -[[package]] -name = "prost-types" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e081b29f63d83a4bc75cfc9f3fe424f9156cf92d8a4f0c9407cce9a1b67327cf" -dependencies = [ - "prost 0.12.1", -] - -[[package]] -name = "protobuf" -version = "2.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" -dependencies = [ - "bytes", -] - -[[package]] -name = "protobuf-src" -version = "1.1.0+21.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7ac8852baeb3cc6fb83b93646fb93c0ffe5d14bf138c945ceb4b9948ee0e3c1" -dependencies = [ - "autotools", -] - -[[package]] -name = "protoc-gen-prost" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10dfa031ad41fdcfb180de73ece3ed076250f1132a13ad6bba218699f612fb95" -dependencies = [ - "once_cell", - "prost 0.11.9", - "prost-build 0.11.9", - "prost-types 0.11.9", - "regex", -] - -[[package]] -name = "protoc-gen-tonic" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "725a07a704f9cf7a956b302c21d81b5516ed5ee6cfbbf827edb69beeaae6cc30" -dependencies = [ - "heck 0.4.1", - "prettyplease 0.1.25", - "prost 0.11.9", - "prost-build 0.11.9", - "prost-types 0.11.9", - "protoc-gen-prost", - "regex", - "syn 1.0.109", - "tonic-build 0.8.4", -] - -[[package]] -name = "ptree" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0de80796b316aec75344095a6d2ef68ec9b8f573b9e7adc821149ba3598e270" -dependencies = [ - "ansi_term", - "atty", - "config", - "directories", - "petgraph 0.6.4", - "serde 1.0.190", - "serde-value", - "tint", -] - -[[package]] -name = "quanta" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17e662a7a8291a865152364c20c7abc5e60486ab2001e8ec10b24862de0b9ab" -dependencies = [ - "crossbeam-utils", - "libc", - "mach2", - "once_cell", - "raw-cpuid", - "wasi 0.11.0+wasi-snapshot-preview1", - "web-sys", - "winapi", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quick-xml" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" -dependencies = [ - "memchr", - "serde 1.0.190", -] - -[[package]] -name = "quinn" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" -dependencies = [ - "bytes", - "futures-io", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls 0.21.8", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "quinn-proto" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c78e758510582acc40acb90458401172d41f1016f8c9dde89e49677afb7eec1" -dependencies = [ - "bytes", - "rand 0.8.5", - "ring 0.16.20", - "rustc-hash", - "rustls 0.21.8", - "slab", - "thiserror", - "tinyvec", - "tracing", -] - -[[package]] -name = "quinn-udp" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" -dependencies = [ - "bytes", - "libc", - "socket2 0.5.5", - "tracing", - "windows-sys 0.48.0", -] - -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -dependencies = [ - "proc-macro2 0.4.30", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2 1.0.69", -] - -[[package]] -name = "radium" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[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 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.10", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "rand_xoshiro" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" -dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "random-manager" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2557c07161d4d805cd7e711d0648b292be9e727d9678d078bab052278cd1b71" -dependencies = [ - "bs58 0.4.0", - "lazy_static 1.4.0", - "primitive-types 0.12.2", - "rand 0.8.5", - "ring 0.16.20", -] - -[[package]] -name = "raw-cpuid" -version = "10.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "rayon" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "rcgen" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" -dependencies = [ - "pem", - "ring 0.16.20", - "time", - "yasna", -] - -[[package]] -name = "rcgen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" -dependencies = [ - "pem", - "ring 0.16.20", - "time", - "x509-parser 0.14.0", - "yasna", -] - -[[package]] -name = "readonly" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f439da1766942fe069954da6058b2e6c1760eb878bae76f5be9fc29f56f574" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "real_tokio" -version = "1.28.1" -source = "git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e4693500118d5e79ce098ee6dfc2c48f3ef19e45#e4693500118d5e79ce098ee6dfc2c48f3ef19e45" -dependencies = [ - "autocfg", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot 0.12.1", - "pin-project-lite", - "signal-hook-registry", - "socket2 0.4.10", - "tokio-macros 2.1.0 (git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e4693500118d5e79ce098ee6dfc2c48f3ef19e45)", - "windows-sys 0.48.0", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_users" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" -dependencies = [ - "getrandom 0.2.10", - "libredox", - "thiserror", -] - -[[package]] -name = "ref-cast" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acde58d073e9c79da00f2b5b84eed919c8326832648a5b109b3fce1bb1175280" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "regex" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.3", - "regex-syntax 0.8.2", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.2", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - -[[package]] -name = "reqwest" -version = "0.11.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" -dependencies = [ - "base64 0.21.5", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-rustls 0.24.2", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding 2.3.0", - "pin-project-lite", - "rustls 0.21.8", - "rustls-pemfile", - "serde 1.0.190", - "serde_json", - "serde_urlencoded", - "system-configuration", - "tokio", - "tokio-native-tls", - "tokio-rustls 0.24.1", - "tokio-util 0.7.10", - "tower-service", - "url 2.4.1", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "webpki-roots 0.25.2", - "winreg", -] - -[[package]] -name = "retain_mut" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c31b5c4033f8fdde8700e4657be2c497e7288f01515be52168c631e2e4d4086" - -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint 0.4.9", - "hmac 0.12.1", - "zeroize", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac 0.12.1", - "subtle", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", -] - -[[package]] -name = "ring" -version = "0.17.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" -dependencies = [ - "cc", - "getrandom 0.2.10", - "libc", - "spin 0.9.8", - "untrusted 0.9.0", - "windows-sys 0.48.0", -] - -[[package]] -name = "ripemd" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "rlp" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" -dependencies = [ - "bytes", - "rlp-derive", - "rustc-hex", -] - -[[package]] -name = "rlp-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "roaring" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6106b5cf8587f5834158895e9715a3c6c9716c8aefab57f1f7680917191c7873" -dependencies = [ - "bytemuck", - "byteorder", - "retain_mut", -] - -[[package]] -name = "rocksdb" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" -dependencies = [ - "libc", - "librocksdb-sys", -] - -[[package]] -name = "rsa" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a77d189da1fee555ad95b7e50e7457d91c0e089ec68ca69ad2989413bbdab4" -dependencies = [ - "byteorder", - "digest 0.10.7", - "num-bigint-dig", - "num-integer", - "num-iter", - "num-traits 0.2.17", - "pkcs1 0.4.1", - "pkcs8 0.9.0", - "rand_core 0.6.4", - "sha2 0.10.8", - "signature 2.1.0", - "subtle", - "zeroize", -] - -[[package]] -name = "rsa" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ef35bf3e7fe15a53c4ab08a998e42271eab13eb0db224126bc7bc4c4bad96d" -dependencies = [ - "const-oid", - "digest 0.10.7", - "num-bigint-dig", - "num-integer", - "num-traits 0.2.17", - "pkcs1 0.7.5", - "pkcs8 0.10.2", - "rand_core 0.6.4", - "signature 2.1.0", - "spki 0.7.2", - "subtle", - "zeroize", -] - -[[package]] -name = "rust-embed" -version = "6.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a36224c3276f8c4ebc8c20f158eca7ca4359c8db89991c4925132aaaf6702661" -dependencies = [ - "rust-embed-impl", - "rust-embed-utils", - "walkdir", -] - -[[package]] -name = "rust-embed-impl" -version = "6.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b94b81e5b2c284684141a2fb9e2a31be90638caf040bf9afbc5a0416afe1ac" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "rust-embed-utils", - "syn 2.0.39", - "walkdir", -] - -[[package]] -name = "rust-embed-utils" -version = "7.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d38ff6bf570dc3bb7100fce9f7b60c33fa71d80e88da3f2580df4ff2bdded74" -dependencies = [ - "sha2 0.10.8", - "walkdir", -] - -[[package]] -name = "rust-ini" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[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.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom 7.1.3", -] - -[[package]] -name = "rustix" -version = "0.38.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" -dependencies = [ - "bitflags 2.4.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustls" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" -dependencies = [ - "log", - "ring 0.16.20", - "sct", - "webpki", -] - -[[package]] -name = "rustls" -version = "0.21.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" -dependencies = [ - "log", - "ring 0.17.5", - "rustls-webpki", - "sct", -] - -[[package]] -name = "rustls-native-certs" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" -dependencies = [ - "openssl-probe", - "rustls-pemfile", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" -dependencies = [ - "base64 0.21.5", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring 0.17.5", - "untrusted 0.9.0", -] - -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - -[[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.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scale-info" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" -dependencies = [ - "cfg-if", - "derive_more", - "parity-scale-codec 3.6.5", - "scale-info-derive", -] - -[[package]] -name = "scale-info-derive" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" -dependencies = [ - "proc-macro-crate 1.1.3", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "schannel" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "schemars" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c" -dependencies = [ - "dyn-clone", - "either", - "schemars_derive", - "serde 1.0.190", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "serde_derive_internals", - "syn 1.0.109", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring 0.17.5", - "untrusted 0.9.0", -] - -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct 0.1.1", - "der 0.6.1", - "generic-array", - "subtle", - "zeroize", -] - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct 0.2.0", - "der 0.7.8", - "generic-array", - "pkcs8 0.10.2", - "subtle", - "zeroize", -] - -[[package]] -name = "secp256k1" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" -dependencies = [ - "bitcoin_hashes", - "rand 0.8.5", - "secp256k1-sys", -] - -[[package]] -name = "secp256k1-sys" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" -dependencies = [ - "cc", -] - -[[package]] -name = "security-framework" -version = "2.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" - -[[package]] -name = "serde" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" - -[[package]] -name = "serde" -version = "1.0.190" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-generate" -version = "0.20.6" -source = "git+https://github.com/aptos-labs/serde-reflection?rev=839aed62a20ddccf043c08961cfe74875741ccba#839aed62a20ddccf043c08961cfe74875741ccba" -dependencies = [ - "bcs 0.1.6", - "bincode", - "heck 0.3.3", - "include_dir 0.6.2", - "maplit", - "serde 1.0.190", - "serde-reflection 0.3.5", - "serde_bytes", - "serde_yaml 0.8.26", - "structopt", - "textwrap 0.13.4", -] - -[[package]] -name = "serde-hjson" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" -dependencies = [ - "lazy_static 1.4.0", - "num-traits 0.1.43", - "regex", - "serde 0.8.23", -] - -[[package]] -name = "serde-name" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12c47087018ec281d1cdab673d36aea22d816b54d498264029c05d5fa1910da6" -dependencies = [ - "serde 1.0.190", - "thiserror", -] - -[[package]] -name = "serde-name" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b5b14ebbcc4e4f2b3642fa99c388649da58d1dc3308c7d109f39f565d1710f0" -dependencies = [ - "serde 1.0.190", - "thiserror", -] - -[[package]] -name = "serde-reflection" -version = "0.3.5" -source = "git+https://github.com/aptos-labs/serde-reflection?rev=839aed62a20ddccf043c08961cfe74875741ccba#839aed62a20ddccf043c08961cfe74875741ccba" -dependencies = [ - "once_cell", - "serde 1.0.190", - "thiserror", -] - -[[package]] -name = "serde-reflection" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05a5f801ac62a51a49d378fdb3884480041b99aced450b28990673e8ff99895" -dependencies = [ - "once_cell", - "serde 1.0.190", - "thiserror", -] - -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float 2.10.1", - "serde 1.0.190", -] - -[[package]] -name = "serde_bytes" -version = "0.11.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" -dependencies = [ - "serde 1.0.190", -] - -[[package]] -name = "serde_derive" -version = "1.0.190" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "serde_derive_internals" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "serde_json" -version = "1.0.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" -dependencies = [ - "indexmap 2.1.0", - "itoa", - "ryu", - "serde 1.0.190", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" -dependencies = [ - "itoa", - "serde 1.0.190", -] - -[[package]] -name = "serde_repr" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[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 1.0.190", -] - -[[package]] -name = "serde_with" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" -dependencies = [ - "base64 0.13.1", - "chrono", - "hex", - "indexmap 1.9.3", - "serde 1.0.190", - "serde_json", - "serde_with_macros 2.3.3", - "time", -] - -[[package]] -name = "serde_with" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" -dependencies = [ - "base64 0.21.5", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.1.0", - "serde 1.0.190", - "serde_json", - "serde_with_macros 3.4.0", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" -dependencies = [ - "darling 0.20.3", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "serde_with_macros" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" -dependencies = [ - "darling 0.20.3", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "serde_yaml" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" -dependencies = [ - "indexmap 1.9.3", - "ryu", - "serde 1.0.190", - "yaml-rust", -] - -[[package]] -name = "serde_yaml" -version = "0.9.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" -dependencies = [ - "indexmap 2.1.0", - "itoa", - "ryu", - "serde 1.0.190", - "unsafe-libyaml", -] - -[[package]] -name = "sha-1" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[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.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "keccak", - "opaque-debug", -] - -[[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 = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static 1.4.0", -] - -[[package]] -name = "shared-crypto" -version = "0.0.0-canonical-sui" -dependencies = [ - "bcs 0.1.6", - "eyre", - "fastcrypto", - "serde 1.0.190", - "serde_repr", - "workspace-hack", -] - -[[package]] -name = "shlex" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" - -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", -] - -[[package]] -name = "signature" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" -dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", -] - -[[package]] -name = "similar" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597" - -[[package]] -name = "simplelog" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bc0ffd69814a9b251d43afcabf96dad1b29f5028378056257be9e3fecc9f720" -dependencies = [ - "chrono", - "log", - "termcolor", -] - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - -[[package]] -name = "sized-chunks" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" -dependencies = [ - "bitmaps", - "typenum", -] - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "slip10_ed25519" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be0ff28bf14f9610a342169084e87a4f435ad798ec528dc7579a3678fa9dc9a" -dependencies = [ - "hmac-sha512", -] - -[[package]] -name = "slug" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd94acec9c8da640005f8e135a39fc0372e74535e6b368b7a04b875f784c8c4" -dependencies = [ - "deunicode", - "wasm-bindgen", -] - -[[package]] -name = "smallvec" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" - -[[package]] -name = "smawk" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" - -[[package]] -name = "snafu" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" -dependencies = [ - "doc-comment", - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" -dependencies = [ - "heck 0.4.1", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "snap" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" - -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "soketto" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" -dependencies = [ - "base64 0.13.1", - "bytes", - "futures 0.3.28", - "http", - "httparse", - "log", - "rand 0.8.5", - "sha-1", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der 0.6.1", -] - -[[package]] -name = "spki" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" -dependencies = [ - "base64ct", - "der 0.7.8", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "standalone-sui-subnet" -version = "0.1.0" -dependencies = [ - "anyhow", - "aptos-types", - "async-channel", - "avalanche-types", - "base64 0.21.5", - "bcs 0.1.6", - "bytes", - "chrono", - "derivative", - "dirs 5.0.1", - "env_logger", - "futures 0.3.28", - "hex", - "jsonrpc-core", - "jsonrpc-core-client", - "jsonrpc-derive", - "log", - "movement-sdk", - "rand 0.8.5", - "serde 1.0.190", - "serde_json", - "serde_with 2.3.3", - "sui-adapter-latest", - "sui-types", - "tokio", - "tonic 0.8.3", -] - -[[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.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap 2.34.0", - "lazy_static 1.4.0", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck 0.4.1", - "proc-macro2 1.0.69", - "quote 1.0.33", - "rustversion", - "syn 1.0.109", -] - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "subtle-ng" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" - -[[package]] -name = "sui-adapter-latest" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "bcs 0.1.6", - "leb128", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-utils 0.1.0-canonical-sui", - "move-bytecode-verifier 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-vm-config", - "move-vm-profiler", - "move-vm-runtime 0.1.0-canonical-sui", - "move-vm-types 0.1.0-canonical-sui", - "parking_lot 0.12.1", - "serde 1.0.190", - "sui-macros", - "sui-move-natives-latest", - "sui-protocol-config", - "sui-types", - "sui-verifier-latest", - "tracing", - "workspace-hack", -] - -[[package]] -name = "sui-adapter-v0" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "bcs 0.1.6", - "leb128", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-utils 0.1.0-canonical-sui", - "move-bytecode-verifier-v0", - "move-core-types 0.0.4-canonical-sui", - "move-vm-config", - "move-vm-profiler", - "move-vm-runtime-v0", - "move-vm-types 0.1.0-canonical-sui", - "once_cell", - "parking_lot 0.12.1", - "serde 1.0.190", - "sui-macros", - "sui-move-natives-v0", - "sui-protocol-config", - "sui-types", - "sui-verifier-v0", - "tracing", - "workspace-hack", -] - -[[package]] -name = "sui-adapter-v1" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "bcs 0.1.6", - "leb128", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-utils 0.1.0-canonical-sui", - "move-bytecode-verifier-v1", - "move-core-types 0.0.4-canonical-sui", - "move-vm-config", - "move-vm-profiler", - "move-vm-runtime-v1", - "move-vm-types 0.1.0-canonical-sui", - "parking_lot 0.12.1", - "serde 1.0.190", - "sui-macros", - "sui-move-natives-v1", - "sui-protocol-config", - "sui-types", - "sui-verifier-v1", - "tracing", - "workspace-hack", -] - -[[package]] -name = "sui-archival" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "byteorder", - "bytes", - "fastcrypto", - "futures 0.3.28", - "indicatif", - "num_enum", - "object_store", - "prometheus", - "rand 0.8.5", - "serde 1.0.190", - "sui-config", - "sui-simulator", - "sui-storage", - "sui-types", - "tokio", - "tracing", - "workspace-hack", -] - -[[package]] -name = "sui-config" -version = "0.0.0-canonical-sui" -dependencies = [ - "anemo", - "anyhow", - "bcs 0.1.6", - "csv", - "dirs 4.0.0", - "fastcrypto", - "narwhal-config", - "once_cell", - "prometheus", - "rand 0.8.5", - "serde 1.0.190", - "serde_with 2.3.3", - "serde_yaml 0.8.26", - "sui-keys", - "sui-protocol-config", - "sui-simulator", - "sui-storage", - "sui-types", - "tracing", - "workspace-hack", -] - -[[package]] -name = "sui-core" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "arc-swap", - "async-trait", - "bcs 0.1.6", - "bytes", - "chrono", - "dashmap", - "either", - "enum_dispatch", - "eyre", - "fastcrypto", - "fastcrypto-zkp", - "futures 0.3.28", - "im", - "indexmap 1.9.3", - "itertools 0.10.5", - "lru", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-utils 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-package 0.1.0-canonical-sui", - "move-symbol-pool 0.1.0-canonical-sui", - "mysten-common", - "mysten-metrics", - "mysten-network", - "mysticeti-core", - "narwhal-config", - "narwhal-crypto", - "narwhal-executor", - "narwhal-network", - "narwhal-node", - "narwhal-test-utils", - "narwhal-types", - "narwhal-worker", - "num_cpus", - "object_store", - "once_cell", - "parking_lot 0.12.1", - "prometheus", - "rand 0.8.5", - "rocksdb", - "scopeguard", - "serde 1.0.190", - "serde_json", - "serde_with 2.3.3", - "shared-crypto", - "signature 1.6.4", - "sui-archival", - "sui-config", - "sui-execution", - "sui-framework", - "sui-genesis-builder", - "sui-json-rpc-types", - "sui-macros", - "sui-move-build", - "sui-network", - "sui-protocol-config", - "sui-simulator", - "sui-storage", - "sui-swarm-config", - "sui-transaction-checks", - "sui-types", - "tap", - "telemetry-subscribers", - "tempfile", - "thiserror", - "tokio", - "tokio-retry", - "tokio-stream", - "tracing", - "typed-store", - "typed-store-derive", - "workspace-hack", - "zeroize", -] - -[[package]] -name = "sui-enum-compat-util" -version = "0.1.0-canonical-sui" -dependencies = [ - "serde_yaml 0.8.26", - "workspace-hack", -] - -[[package]] -name = "sui-execution" -version = "0.1.0-canonical-sui" -dependencies = [ - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-verifier 0.1.0-canonical-sui", - "move-bytecode-verifier-v0", - "move-bytecode-verifier-v1", - "move-vm-config", - "move-vm-runtime 0.1.0-canonical-sui", - "move-vm-runtime-v0", - "move-vm-runtime-v1", - "sui-adapter-latest", - "sui-adapter-v0", - "sui-adapter-v1", - "sui-move-natives-latest", - "sui-move-natives-v0", - "sui-move-natives-v1", - "sui-protocol-config", - "sui-types", - "sui-verifier-latest", - "sui-verifier-v0", - "sui-verifier-v1", - "workspace-hack", -] - -[[package]] -name = "sui-framework" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "bcs 0.1.6", - "move-binary-format 0.0.3-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-package 0.1.0-canonical-sui", - "once_cell", - "serde 1.0.190", - "sui-move-build", - "sui-types", - "tracing", - "workspace-hack", -] - -[[package]] -name = "sui-framework-snapshot" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "bcs 0.1.6", - "git-version", - "serde 1.0.190", - "serde_json", - "sui-framework", - "sui-protocol-config", - "sui-types", - "workspace-hack", -] - -[[package]] -name = "sui-genesis-builder" -version = "0.0.0-canonical-sui" -dependencies = [ - "anyhow", - "bcs 0.1.6", - "camino", - "fastcrypto", - "move-binary-format 0.0.3-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "prometheus", - "rand 0.8.5", - "serde 1.0.190", - "serde_with 2.3.3", - "serde_yaml 0.8.26", - "shared-crypto", - "sui-config", - "sui-execution", - "sui-framework", - "sui-framework-snapshot", - "sui-protocol-config", - "sui-simulator", - "sui-types", - "tempfile", - "tracing", - "workspace-hack", -] - -[[package]] -name = "sui-json" -version = "0.0.0-canonical-sui" -dependencies = [ - "anyhow", - "bcs 0.1.6", - "fastcrypto", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-utils 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "schemars", - "serde 1.0.190", - "serde_json", - "sui-framework", - "sui-types", - "workspace-hack", -] - -[[package]] -name = "sui-json-rpc" -version = "0.0.0-canonical-sui" -dependencies = [ - "anyhow", - "arc-swap", - "async-trait", - "axum", - "bcs 0.1.6", - "cached", - "eyre", - "fastcrypto", - "futures 0.3.28", - "hyper", - "itertools 0.10.5", - "jsonrpsee", - "linked-hash-map", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-utils 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-package 0.1.0-canonical-sui", - "mysten-metrics", - "once_cell", - "prometheus", - "serde 1.0.190", - "serde_json", - "shared-crypto", - "signature 1.6.4", - "sui-core", - "sui-json", - "sui-json-rpc-types", - "sui-open-rpc", - "sui-open-rpc-macros", - "sui-protocol-config", - "sui-storage", - "sui-transaction-builder", - "sui-types", - "tap", - "thiserror", - "tokio", - "tower", - "tower-http", - "tracing", - "typed-store-error", - "workspace-hack", -] - -[[package]] -name = "sui-json-rpc-types" -version = "0.0.0-canonical-sui" -dependencies = [ - "anyhow", - "bcs 0.1.6", - "colored", - "enum_dispatch", - "fastcrypto", - "itertools 0.10.5", - "json_to_table", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-utils 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "mysten-metrics", - "schemars", - "serde 1.0.190", - "serde_json", - "serde_with 2.3.3", - "sui-enum-compat-util", - "sui-json", - "sui-macros", - "sui-protocol-config", - "sui-types", - "tabled", - "tracing", - "workspace-hack", -] - -[[package]] -name = "sui-keys" -version = "0.0.0-canonical-sui" -dependencies = [ - "anyhow", - "bip32", - "fastcrypto", - "rand 0.8.5", - "serde 1.0.190", - "serde_json", - "shared-crypto", - "signature 1.6.4", - "slip10_ed25519", - "sui-types", - "tiny-bip39", - "workspace-hack", -] - -[[package]] -name = "sui-macros" -version = "0.7.0-canonical-sui" -dependencies = [ - "futures 0.3.28", - "once_cell", - "sui-proc-macros", - "tracing", - "workspace-hack", -] - -[[package]] -name = "sui-move-build" -version = "0.0.0-canonical-sui" -dependencies = [ - "anyhow", - "fastcrypto", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-utils 0.1.0-canonical-sui", - "move-bytecode-verifier 0.1.0-canonical-sui", - "move-command-line-common 0.1.0-canonical-sui", - "move-compiler 0.0.1-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-ir-types 0.1.0-canonical-sui", - "move-package 0.1.0-canonical-sui", - "move-symbol-pool 0.1.0-canonical-sui", - "serde-reflection 0.3.6", - "sui-types", - "sui-verifier-latest", - "tempfile", - "workspace-hack", -] - -[[package]] -name = "sui-move-natives-latest" -version = "0.1.0-canonical-sui" -dependencies = [ - "bcs 0.1.6", - "better_any", - "fastcrypto", - "fastcrypto-zkp", - "linked-hash-map", - "move-binary-format 0.0.3-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-stdlib", - "move-vm-runtime 0.1.0-canonical-sui", - "move-vm-types 0.1.0-canonical-sui", - "smallvec", - "sui-protocol-config", - "sui-types", - "workspace-hack", -] - -[[package]] -name = "sui-move-natives-v0" -version = "0.1.0-canonical-sui" -dependencies = [ - "bcs 0.1.6", - "better_any", - "fastcrypto", - "fastcrypto-zkp", - "linked-hash-map", - "move-binary-format 0.0.3-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-stdlib-v0", - "move-vm-runtime-v0", - "move-vm-types 0.1.0-canonical-sui", - "smallvec", - "sui-protocol-config", - "sui-types", - "workspace-hack", -] - -[[package]] -name = "sui-move-natives-v1" -version = "0.1.0-canonical-sui" -dependencies = [ - "bcs 0.1.6", - "better_any", - "fastcrypto", - "fastcrypto-zkp", - "linked-hash-map", - "move-binary-format 0.0.3-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-stdlib-v1", - "move-vm-runtime-v1", - "move-vm-types 0.1.0-canonical-sui", - "smallvec", - "sui-protocol-config", - "sui-types", - "workspace-hack", -] - -[[package]] -name = "sui-network" -version = "0.0.0-canonical-sui" -dependencies = [ - "anemo", - "anemo-build", - "anemo-tower", - "anyhow", - "dashmap", - "futures 0.3.28", - "governor", - "mysten-metrics", - "mysten-network", - "prometheus", - "rand 0.8.5", - "serde 1.0.190", - "sui-archival", - "sui-config", - "sui-storage", - "sui-swarm-config", - "sui-types", - "tap", - "tokio", - "tonic 0.10.2", - "tonic-build 0.10.2", - "tower", - "tracing", - "workspace-hack", -] - -[[package]] -name = "sui-open-rpc" -version = "1.15.0-canonical-sui" -dependencies = [ - "bcs 0.1.6", - "schemars", - "serde 1.0.190", - "serde_json", - "versions", - "workspace-hack", -] - -[[package]] -name = "sui-open-rpc-macros" -version = "0.1.0-canonical-sui" -dependencies = [ - "derive-syn-parse", - "itertools 0.10.5", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", - "unescape", - "workspace-hack", -] - -[[package]] -name = "sui-proc-macros" -version = "0.7.0-canonical-sui" -dependencies = [ - "msim-macros", - "proc-macro2 1.0.69", - "quote 1.0.33", - "sui-enum-compat-util", - "syn 2.0.39", - "workspace-hack", -] - -[[package]] -name = "sui-protocol-config" -version = "0.1.0-canonical-sui" -dependencies = [ - "clap 4.4.10", - "insta", - "schemars", - "serde 1.0.190", - "serde_with 2.3.3", - "sui-protocol-config-macros", - "tracing", - "workspace-hack", -] - -[[package]] -name = "sui-protocol-config-macros" -version = "0.1.0-canonical-sui" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", - "workspace-hack", -] - -[[package]] -name = "sui-sdk" -version = "1.15.0-canonical-sui" -dependencies = [ - "anyhow", - "async-trait", - "bcs 0.1.6", - "clap 4.4.10", - "colored", - "fastcrypto", - "futures 0.3.28", - "futures-core", - "jsonrpsee", - "move-core-types 0.0.4-canonical-sui", - "reqwest", - "serde 1.0.190", - "serde_json", - "serde_with 2.3.3", - "shared-crypto", - "sui-config", - "sui-json", - "sui-json-rpc", - "sui-json-rpc-types", - "sui-keys", - "sui-transaction-builder", - "sui-types", - "thiserror", - "tokio", - "tracing", - "workspace-hack", -] - -[[package]] -name = "sui-simulator" -version = "0.7.0-canonical-sui" -dependencies = [ - "anemo", - "anemo-tower", - "fastcrypto", - "lru", - "move-package 0.1.0-canonical-sui", - "msim", - "narwhal-network", - "rand 0.8.5", - "sui-framework", - "sui-move-build", - "sui-types", - "telemetry-subscribers", - "tempfile", - "tower", - "tracing", - "workspace-hack", -] - -[[package]] -name = "sui-storage" -version = "0.1.0-canonical-sui" -dependencies = [ - "anyhow", - "async-trait", - "backoff", - "base64-url", - "bcs 0.1.6", - "byteorder", - "bytes", - "chrono", - "clap 4.4.10", - "eyre", - "fastcrypto", - "futures 0.3.28", - "hyper", - "hyper-rustls 0.24.2", - "indicatif", - "integer-encoding", - "itertools 0.10.5", - "lru", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-utils 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "mysten-metrics", - "num_enum", - "object_store", - "parking_lot 0.12.1", - "percent-encoding 2.3.0", - "prometheus", - "reqwest", - "rocksdb", - "serde 1.0.190", - "sui-json-rpc-types", - "sui-protocol-config", - "sui-types", - "tap", - "telemetry-subscribers", - "tempfile", - "tokio", - "tracing", - "typed-store", - "typed-store-derive", - "url 2.4.1", - "workspace-hack", - "zstd", -] - -[[package]] -name = "sui-swarm-config" -version = "0.0.0-canonical-sui" -dependencies = [ - "anemo", - "anyhow", - "fastcrypto", - "move-bytecode-utils 0.1.0-canonical-sui", - "narwhal-config", - "prometheus", - "rand 0.8.5", - "serde 1.0.190", - "serde_with 2.3.3", - "serde_yaml 0.8.26", - "shared-crypto", - "sui-config", - "sui-genesis-builder", - "sui-protocol-config", - "sui-simulator", - "sui-types", - "tempfile", - "tracing", - "workspace-hack", -] - -[[package]] -name = "sui-test-transaction-builder" -version = "0.1.0-canonical-sui" -dependencies = [ - "bcs 0.1.6", - "move-core-types 0.0.4-canonical-sui", - "shared-crypto", - "sui-genesis-builder", - "sui-move-build", - "sui-sdk", - "sui-types", - "workspace-hack", -] - -[[package]] -name = "sui-transaction-builder" -version = "0.0.0-canonical-sui" -dependencies = [ - "anyhow", - "async-trait", - "bcs 0.1.6", - "futures 0.3.28", - "move-binary-format 0.0.3-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "sui-json", - "sui-json-rpc-types", - "sui-protocol-config", - "sui-types", - "workspace-hack", -] - -[[package]] -name = "sui-transaction-checks" -version = "0.1.0-canonical-sui" -dependencies = [ - "fastcrypto-zkp", - "once_cell", - "sui-config", - "sui-execution", - "sui-macros", - "sui-protocol-config", - "sui-types", - "tracing", - "workspace-hack", -] - -[[package]] -name = "sui-types" -version = "0.1.0-canonical-sui" -dependencies = [ - "anemo", - "anyhow", - "bcs 0.1.6", - "bincode", - "byteorder", - "derivative", - "derive_more", - "enum_dispatch", - "eyre", - "fastcrypto", - "fastcrypto-zkp", - "im", - "indexmap 1.9.3", - "itertools 0.10.5", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-utils 0.1.0-canonical-sui", - "move-command-line-common 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-disassembler 0.1.0-canonical-sui", - "move-ir-types 0.1.0-canonical-sui", - "move-vm-profiler", - "move-vm-test-utils 0.1.0-canonical-sui", - "move-vm-types 0.1.0-canonical-sui", - "mysten-metrics", - "mysten-network", - "narwhal-config", - "narwhal-crypto", - "once_cell", - "prometheus", - "proptest", - "proptest-derive", - "rand 0.8.5", - "roaring", - "schemars", - "serde 1.0.190", - "serde-name 0.2.1", - "serde_json", - "serde_with 2.3.3", - "shared-crypto", - "signature 1.6.4", - "static_assertions", - "strum", - "strum_macros", - "sui-enum-compat-util", - "sui-macros", - "sui-protocol-config", - "tap", - "thiserror", - "tonic 0.10.2", - "tracing", - "typed-store-error", - "workspace-hack", -] - -[[package]] -name = "sui-verifier-latest" -version = "0.1.0-canonical-sui" -dependencies = [ - "move-abstract-stack", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-utils 0.1.0-canonical-sui", - "move-bytecode-verifier 0.1.0-canonical-sui", - "move-core-types 0.0.4-canonical-sui", - "move-vm-config", - "sui-types", - "workspace-hack", -] - -[[package]] -name = "sui-verifier-v0" -version = "0.1.0-canonical-sui" -dependencies = [ - "move-abstract-stack", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-utils 0.1.0-canonical-sui", - "move-bytecode-verifier-v0", - "move-core-types 0.0.4-canonical-sui", - "move-vm-config", - "sui-protocol-config", - "sui-types", - "workspace-hack", -] - -[[package]] -name = "sui-verifier-v1" -version = "0.1.0-canonical-sui" -dependencies = [ - "move-abstract-stack", - "move-binary-format 0.0.3-canonical-sui", - "move-bytecode-utils 0.1.0-canonical-sui", - "move-bytecode-verifier-v1", - "move-core-types 0.0.4-canonical-sui", - "move-vm-config", - "sui-types", - "workspace-hack", -] - -[[package]] -name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid 0.1.0", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", - "unicode-xid 0.2.4", -] - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tabled" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce69a5028cd9576063ec1f48edb2c75339fd835e6094ef3e05b3a079bf594a6" -dependencies = [ - "papergrid", - "tabled_derive", - "unicode-width", -] - -[[package]] -name = "tabled_derive" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99f688a08b54f4f02f0a3c382aefdb7884d3d69609f785bd253dc033243e3fe4" -dependencies = [ - "heck 0.4.1", - "proc-macro-error", - "proc-macro2 1.0.69", - "quote 1.0.33", - "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 = "telemetry-subscribers" -version = "0.2.0-canonical-sui" -dependencies = [ - "atomic_float", - "bytes", - "bytes-varint", - "clap 4.4.10", - "crossterm", - "futures 0.3.28", - "once_cell", - "opentelemetry", - "opentelemetry-otlp", - "opentelemetry-proto", - "opentelemetry_api", - "prometheus", - "prost 0.11.9", - "tokio", - "tonic 0.9.2", - "tracing", - "tracing-appender", - "tracing-opentelemetry", - "tracing-subscriber", - "workspace-hack", -] - -[[package]] -name = "tempfile" -version = "3.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall 0.4.1", - "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "tera" -version = "1.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970dff17c11e884a4a09bc76e3a17ef71e01bb13447a11e85226e254fe6d10b8" -dependencies = [ - "chrono", - "chrono-tz", - "globwalk", - "humansize", - "lazy_static 1.4.0", - "percent-encoding 2.3.0", - "pest", - "pest_derive", - "rand 0.8.5", - "regex", - "serde 1.0.190", - "serde_json", - "slug", - "unic-segment", -] - -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "terminal_size" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" -dependencies = [ - "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "termtree" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "textwrap" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd05616119e612a8041ef58f2b578906cc2531a6069047ae092cfb86a325d835" -dependencies = [ - "smawk", - "unicode-width", -] - -[[package]] -name = "textwrap" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" -dependencies = [ - "smawk", - "unicode-linebreak", - "unicode-width", -] - -[[package]] -name = "thiserror" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "thread_local" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - -[[package]] -name = "time" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" -dependencies = [ - "deranged", - "itoa", - "powerfmt", - "serde 1.0.190", - "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.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" -dependencies = [ - "time-core", -] - -[[package]] -name = "tint" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7af24570664a3074673dbbf69a65bdae0ae0b72f2949b1adfbacb736ee4d6896" -dependencies = [ - "lazy_static 0.2.11", -] - -[[package]] -name = "tiny-bip39" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" -dependencies = [ - "anyhow", - "hmac 0.12.1", - "once_cell", - "pbkdf2", - "rand 0.8.5", - "rustc-hash", - "sha2 0.10.8", - "thiserror", - "unicode-normalization", - "wasm-bindgen", - "zeroize", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -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.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot 0.12.1", - "pin-project-lite", - "signal-hook-registry", - "socket2 0.5.5", - "tokio-macros 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e4693500118d5e79ce098ee6dfc2c48f3ef19e45#e4693500118d5e79ce098ee6dfc2c48f3ef19e45" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[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-retry" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f" -dependencies = [ - "pin-project", - "rand 0.8.5", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" -dependencies = [ - "rustls 0.20.9", - "tokio", - "webpki", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls 0.21.8", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util 0.7.10", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite", -] - -[[package]] -name = "tokio-util" -version = "0.7.7" -source = "git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e4693500118d5e79ce098ee6dfc2c48f3ef19e45#e4693500118d5e79ce098ee6dfc2c48f3ef19e45" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "futures-util", - "hashbrown 0.12.3", - "pin-project-lite", - "real_tokio", - "slab", - "tracing", -] - -[[package]] -name = "tokio-util" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde 1.0.190", -] - -[[package]] -name = "tonic" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" -dependencies = [ - "async-stream", - "async-trait", - "axum", - "base64 0.13.1", - "bytes", - "flate2", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-timeout", - "percent-encoding 2.3.0", - "pin-project", - "prost 0.11.9", - "prost-derive 0.11.9", - "tokio", - "tokio-stream", - "tokio-util 0.7.10", - "tower", - "tower-layer", - "tower-service", - "tracing", - "tracing-futures", -] - -[[package]] -name = "tonic" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" -dependencies = [ - "async-trait", - "axum", - "base64 0.21.5", - "bytes", - "flate2", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-timeout", - "percent-encoding 2.3.0", - "pin-project", - "prost 0.11.9", - "tokio", - "tokio-stream", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tonic" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" -dependencies = [ - "async-stream", - "async-trait", - "axum", - "base64 0.21.5", - "bytes", - "h2", - "http", - "http-body", - "hyper", - "hyper-timeout", - "percent-encoding 2.3.0", - "pin-project", - "prost 0.12.1", - "rustls 0.21.8", - "rustls-pemfile", - "tokio", - "tokio-rustls 0.24.1", - "tokio-stream", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tonic-build" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" -dependencies = [ - "prettyplease 0.1.25", - "proc-macro2 1.0.69", - "prost-build 0.11.9", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "tonic-build" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d021fc044c18582b9a2408cd0dd05b1596e3ecdb5c4df822bb0183545683889" -dependencies = [ - "prettyplease 0.2.15", - "proc-macro2 1.0.69", - "prost-build 0.12.1", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "tonic-health" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080964d45894b90273d2b1dd755fdd114560db8636bb41cea615213c45043c4d" -dependencies = [ - "async-stream", - "prost 0.11.9", - "tokio", - "tokio-stream", - "tonic 0.9.2", -] - -[[package]] -name = "tonic-health" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f80db390246dfb46553481f6024f0082ba00178ea495dbb99e70ba9a4fafb5e1" -dependencies = [ - "async-stream", - "prost 0.12.1", - "tokio", - "tokio-stream", - "tonic 0.10.2", -] - -[[package]] -name = "tonic-reflection" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0543d7092032041fbeac1f2c84304537553421a11a623c2301b12ef0264862c7" -dependencies = [ - "prost 0.11.9", - "prost-types 0.11.9", - "tokio", - "tokio-stream", - "tonic 0.9.2", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "hdrhistogram", - "indexmap 1.9.3", - "pin-project", - "pin-project-lite", - "rand 0.8.5", - "slab", - "tokio", - "tokio-util 0.7.10", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" -dependencies = [ - "async-compression", - "base64 0.13.1", - "bitflags 1.3.2", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", - "httpdate", - "iri-string", - "mime", - "mime_guess", - "percent-encoding 2.3.0", - "pin-project-lite", - "tokio", - "tokio-util 0.7.10", - "tower", - "tower-layer", - "tower-service", - "tracing", - "uuid", -] - -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "trace" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ad0c048e114d19d1140662762bfdb10682f3bc806d8be18af846600214dd9af" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-appender" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e" -dependencies = [ - "crossbeam-channel", - "time", - "tracing-subscriber", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - -[[package]] -name = "tracing-log" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-opentelemetry" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75327c6b667828ddc28f5e3f169036cb793c3f588d83bf0f262a7f062ffed3c8" -dependencies = [ - "once_cell", - "opentelemetry", - "opentelemetry_sdk", - "smallvec", - "tracing", - "tracing-core", - "tracing-log", - "tracing-subscriber", -] - -[[package]] -name = "tracing-serde" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" -dependencies = [ - "serde 1.0.190", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "serde 1.0.190", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", - "time", - "tracing", - "tracing-core", - "tracing-log", - "tracing-serde", -] - -[[package]] -name = "treeline" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "tungstenite" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand 0.8.5", - "sha1", - "thiserror", - "url 2.4.1", - "utf-8", -] - -[[package]] -name = "typed-arena" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" - -[[package]] -name = "typed-store" -version = "0.4.0-canonical-sui" -dependencies = [ - "async-trait", - "bcs 0.1.6", - "bincode", - "collectable", - "eyre", - "fdlimit", - "hdrhistogram", - "itertools 0.10.5", - "msim", - "once_cell", - "ouroboros 0.17.2", - "prometheus", - "rand 0.8.5", - "rocksdb", - "serde 1.0.190", - "sui-macros", - "tap", - "thiserror", - "tokio", - "tracing", - "typed-store-error", - "workspace-hack", -] - -[[package]] -name = "typed-store-derive" -version = "0.3.0-canonical-sui" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.109", - "workspace-hack", -] - -[[package]] -name = "typed-store-error" -version = "0.4.0-canonical-sui" -dependencies = [ - "serde 1.0.190", - "thiserror", - "workspace-hack", -] - -[[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.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" - -[[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 = "unescape" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e" - -[[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-segment" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" -dependencies = [ - "unic-ucd-segment", -] - -[[package]] -name = "unic-ucd-segment" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-linebreak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-width" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" - -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - -[[package]] -name = "unsafe-libyaml" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" - -[[package]] -name = "unsigned-varint" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" -dependencies = [ - "idna 0.1.5", - "matches", - "percent-encoding 1.0.1", -] - -[[package]] -name = "url" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" -dependencies = [ - "form_urlencoded", - "idna 0.4.0", - "percent-encoding 2.3.0", -] - -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "uuid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" -dependencies = [ - "getrandom 0.2.10", - "rand 0.8.5", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "variant_count" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae2faf80ac463422992abf4de234731279c058aaf33171ca70277c98406b124" -dependencies = [ - "quote 1.0.33", - "syn 1.0.109", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "versions" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee97e1d97bd593fb513912a07691b742361b3dd64ad56f2c694ea2dbfe0665d3" -dependencies = [ - "itertools 0.10.5", - "nom 7.1.3", -] - -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - -[[package]] -name = "walkdir" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" -dependencies = [ - "same-file", - "winapi-util", -] - -[[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.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" -dependencies = [ - "quote 1.0.33", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" - -[[package]] -name = "wasm-streams" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" -dependencies = [ - "ring 0.17.5", - "untrusted 0.9.0", -] - -[[package]] -name = "webpki-roots" -version = "0.22.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki", -] - -[[package]] -name = "webpki-roots" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" - -[[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 = "whoami" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "widestring" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.51.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "workspace-hack" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beffa227304dbaea3ad6a06ac674f9bc83a3dec3b7f63eeb442de37e7cb6bb01" - -[[package]] -name = "wyz" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "x25519-dalek" -version = "1.2.0" -dependencies = [ - "curve25519-dalek", - "rand_core 0.5.1", - "zeroize", -] - -[[package]] -name = "x509-parser" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" -dependencies = [ - "asn1-rs", - "base64 0.13.1", - "data-encoding", - "der-parser", - "lazy_static 1.4.0", - "nom 7.1.3", - "oid-registry", - "ring 0.16.20", - "rusticata-macros", - "thiserror", - "time", -] - -[[package]] -name = "x509-parser" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" -dependencies = [ - "asn1-rs", - "data-encoding", - "der-parser", - "lazy_static 1.4.0", - "nom 7.1.3", - "oid-registry", - "rusticata-macros", - "thiserror", - "time", -] - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "yasna" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" -dependencies = [ - "time", -] - -[[package]] -name = "zerocopy" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96f8f25c15a0edc9b07eb66e7e6e97d124c0505435c382fde1ab7ceb188aa956" -dependencies = [ - "byteorder", - "zerocopy-derive 0.6.5", -] - -[[package]] -name = "zerocopy" -version = "0.7.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd369a67c0edfef15010f980c3cbe45d7f651deac2cd67ce097cd801de16557" -dependencies = [ - "zerocopy-derive 0.7.25", -] - -[[package]] -name = "zerocopy-derive" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "855e0f6af9cd72b87d8a6c586f3cb583f5cdcc62c2c80869d8cd7e96fdf7ee20" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f140bda219a26ccc0cdb03dba58af72590c53b22642577d88a927bc5c87d6b" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "zeroize" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" -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 1.0.69", - "quote 1.0.33", - "syn 2.0.39", -] - -[[package]] -name = "zstd" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "6.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/canonical/Cargo.toml b/canonical/Cargo.toml deleted file mode 100644 index a6b93c9ac..000000000 --- a/canonical/Cargo.toml +++ /dev/null @@ -1,67 +0,0 @@ -[workspace] -resolver = "2" -members = [ - - "canonical-move-resolver", - "canonical-move-natives", - - "canonical-block-executor", - - "canonical-types" - -] - -[workspace.package] -version = "0.1.0" -edition = "2021" -license = "MIT OR Apache-2.0" -authors = ["Liam Monninger "] -homepage = "https://www.movementlabs.xyz" -publish = false -repository = "https://github.com/movemntdev/m2" -rust-version = "1.70" - -# testing - -[workspace.dependencies] -# internal -canonical-types = { path = "../canonical-types" } - -# general -anyhow = "1.0.44" -futures = "0.3.17" -rand = "0.8.4" -bcs = "0.1.0" -tempfile = "3.2.0" -tracing = "0.1.37" -tracing-appender = "0.2.2" -tracing-subscriber = { version = "0.3.15", default-features = false, features = [ - "std", - "smallvec", - "fmt", - "ansi", - "time", - "json", - "registry", - "env-filter", -] } -ctor = "0.2.5" - -# aptos -aptos-framework = { path = "../vendors/aptos-core/aptos-move/framework" } -aptos-vm = { path = "../vendors/aptos-core/aptos-move/aptos-vm" } -aptos-types = { path = "../vendors/aptos-core/types" } -aptos-executor = { path = "../vendors/aptos-core/execution/executor" } -aptos-executor-types = { path = "../vendors/aptos-core/execution/executor-types" } -aptos-storage-interface = { path = "../vendors/aptos-core/storage/storage-interface" } - -# sui -sui-adapter-latest = { path = "../vendors/sui/sui-execution/latest/sui-adapter" } -sui-types = { path = "../vendors/sui/crates/sui-types" } -sui-core = { path = "../vendors/sui/crates/sui-core" } -sui-swarm-config = { path = "../vendors/sui/crates/sui-swarm-config" } -sui-test-transaction-builder = { path = "../vendors/sui/crates/sui-test-transaction-builder" } - -# movement -movement-sdk = { path = "../movement-sdk/movement-sdk" } - diff --git a/canonical/README.md b/canonical/README.md deleted file mode 100644 index a30bcb98a..000000000 --- a/canonical/README.md +++ /dev/null @@ -1,68 +0,0 @@ -# Canonical Execution Layer and Supporting Layers -DEPRECATED: This workspace is deprecated. Please see the work out of the `movement-sdk` workspace. - - -© 2023, Movement Labs. All rights reserved. - -## Summary -Provide a Canonical Execution layer that supports behavior analogous to popular Move networks and idioms including Sui and the, currently, internal Movement VM. In addition to the execution layer, provide supporting logic which enables inter-execution-layer compatibility and analogous behavior to the execution layer upstream. - -## Motivation -To create an execution layer that is (a) capable of supporting popular Move idioms, (b) interoperating between different idioms without the need for messaging or bridging, and (c) portable to other consensus and settlement layers via the `movement-sdk`. - - -## Requirements -At the time of the writing, Canonical Execution Layer will support the following "VMs": -- Sui -- Movement - -The support for Sui SHALL include an analogous RPC to the Sui upstream. - -The support for Movement SHALL include an analogous RPC to the Movement upstream. - -The Sui and Movement execution paths SHALL be interoperable. - -The initial deployment of Canonical Execution Layer SHALL be as an Avalanche subnet. That is, consensus and settlement SHALL be handled by the Avalanche consensus engine. - -## User Journeys - -### Canonical Subnet -- Developer can Submit a Sui Transaction to the network for execution and expect that accepted Sui transactions will be executed in a manner analogous to the Sui upstream. -- Developer can Submit a Movement Transaction to the network for execution and expect that accepted Movement transactions are executed in a manner analogous to the Movement upstream. -- Developer can make requests to Sui routes within the Canonical Subnet RPC and received responses analogous to the Sui upstream. -- Developer can make requests to Movement routes within the Canonical Subnet RPC and received responses analogous to the Movement upstream. -- Developer can operate on states altered by Movement transactions via Sui transactions and vice versa. - -## Appendices - -### [A1] Stage 1: Standalone Sui Subnet -#### Summary -To better understand the intricacies of Sui while delivering a new network, this project SHALL begin with the construction of a Sui Avalanche subnet. This subnet shall feature the Sui Block Execution Layer which will ideally later be used to compose the Canonical Execution Layer. - -The requirements for creating analogous structures to the Sui upstream stipulated for the canonical remain firmly in place. - -#### Motivation -- Better understand Sui execution. -- Provide Sui-compatible network. -- Be able to include a Sui execution layer within the `movement-sdk`. -- With the successful extraction of Sui into a Sui Block Executor and other analogous structure, we may be able offer Mysten Labs an implementation of Sui as L2--a goal reported to us via the BD team. - -### [A2] Interoperation via Common Storage -One way to achieve interoperation between different execution layers is to have them share a common move resolver or storage layer. This would allow for the execution layers to operate on the same data. - -**N1**: Synchronization between the execution layers would be required to ensure that the data is consistent. -**N2**: This proposal is conceptually similar to the "transaction multiplexing" first suggested in [MIP-3](https://github.com/movemntdev/MIPs/blob/main/MIPs/mip-3.md) - -![Common Storage](./rsc/canonical_shared_resolver.png) - - -### [A3] Sui Block Execution Layer -In order to achieve Sui-like execution in a block-based consensus network, it has been recommended a point of contact at Mysten Labs that we utilize the checkpoint execution path in the current Sui source. This appears as below: -![Sui Block Executor](./rsc/sui_block_executor.png) - -### [A4] Implementation of Canonical Subnet -#### Summary -The first deployment of the Canonical Execution Layer will be as an Avalanche subnet. The subnet Execution Layer will be composed of the Sui Block Executor and the Movement Block Executor. The subnet will be configured to use the Avalanche consensus engine for consensus and settlement. - -### [A5] Stage 3: Integration of Sui Block Executor and Canonical Execution Layer with Movement-SDK -WIP \ No newline at end of file diff --git a/canonical/canonical-block-executor/Cargo.toml b/canonical/canonical-block-executor/Cargo.toml deleted file mode 100644 index d954e958b..000000000 --- a/canonical/canonical-block-executor/Cargo.toml +++ /dev/null @@ -1,48 +0,0 @@ -[package] -name = "canonical-block-executor" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -avalanche-types = { version = "0.0.398", features = ["subnet", "codec_base64"] } -tokio = { version = "1.25.0", features = ["fs", "rt-multi-thread"] } -tonic = { version = "0.8.3", features = ["gzip"] } -serde = { version = "1.0.152", features = ["derive"] } -serde_json = "1.0.93" # https://github.com/serde-rs/json/releases -serde_with = { version = "2.2.0", features = ["hex"] } -log = "0.4.17" -dirs = "5.0.1" -hex = "0.4.3" -bytes = "1.4.0" -env_logger = "0.10.0" -base64 = { version = "0.21.0" } -chrono = "0.4.23" -derivative = "2.2.0" -jsonrpc-core = "18.0.0" -jsonrpc-core-client = { version = "18.0.0" } -jsonrpc-derive = "18.0.0" -async-channel = "1.9.0" -tracing = { workspace = true } -tracing-subscriber = { workspace = true } -ctor = { workspace = true } - -# general -anyhow = { workspace = true } -futures = { workspace = true } -rand = { workspace = true } -bcs = { workspace = true } -movement-sdk = { workspace = true } -tempfile = { workspace = true } - -# sui -# todo: conflicting rocksdb means we can't use workspace -# todo: likely movement-sdk will move into its own workspace -# todo: once that happens, we can move sui into its own workspace -# todo: we will have to reconcile the two when we begin on the canonical VM -sui-adapter-latest = { workspace = true } -sui-types = { workspace = true } -sui-core = { workspace = true } -sui-swarm-config = { workspace = true } -sui-test-transaction-builder = { workspace = true} diff --git a/canonical/canonical-block-executor/src/canonical_block_executor/mod.rs b/canonical/canonical-block-executor/src/canonical_block_executor/mod.rs deleted file mode 100644 index 4954a489e..000000000 --- a/canonical/canonical-block-executor/src/canonical_block_executor/mod.rs +++ /dev/null @@ -1,71 +0,0 @@ -/*use movement_sdk::{ExecutionLayer}; -use canonical_move_resolver::CanonicalMoveResolver; -use canonical_types::{Transaction, Block};*/ - -pub mod sui_block_executor; - -// todo: this will likely be a wrapper around some kind of -// todo: type-state pattern structs that handle the bootstrapping -// todo: or else we will move that to a higher order -// todo: good thing to consider for the movement_sdk -/*pub struct CanonicalBlockExecutionLayer<'state> { - move_resolver : CanonicalMoveResolver<'state> -} - -impl <'state> CanonicalBlockExecutionLayer<'state> { - - fn get_aptos_vm(){ - unimplemented!(); - } - - fn get_sui_executor(){ - unimplemented!(); - } - - /// Filters the block to just Aptos transactions. - fn get_aptos_block(block : Block){ - unimplemented!(); - } - - /// Filters the block to just Sui transactions. - /// Sui blocks are a fundamentally new construct. - /// Sui does not have blocks in its original form. - /// Concurrency considerations have not been made. - fn get_sui_block(block : Block){ - unimplemented!(); - } - - /// Executes a canonical block. - /// Canonical blocks in V1 are split into their respective transaction blocks. - /// This is done to accommodate a transaction flow that is compatible with their respective RPCs. - /// While Aptos has an original notion of blocks, Sui does not. - /// Aptos transactions will thus be executed concurrently, insofar as Block-STM allows. - /// Sui transactions will for now be executed sequentially. - /// The order of Sui and Aptos execution must be deterministic. - /// For now, we propose Sui transactions are executed first, then Aptos. - /// We can opt for a more balance strategy at a later date. - fn execute(block : Block){ - - // 1. Execute the Sui block. - let sui_block = self.get_sui_block(block); - let sui_executor = self.get_sui_executor(); - - // all sui transactions will be sequential for now - for transaction in sui_block { - - sui_executor.execute_transaction_to_effects( - transaction, - .... - ); - - } - - // 2. Execute the Aptos block - let aptos_block = self.get_aptos_block(block); - let aptos_executor = self.get_aptos_executor(); - aptos_executor.execute_block(aptos_block); - - - } - -}*/ \ No newline at end of file diff --git a/canonical/canonical-block-executor/src/canonical_block_executor/sui_block_executor/README.md b/canonical/canonical-block-executor/src/canonical_block_executor/sui_block_executor/README.md deleted file mode 100644 index 276417aac..000000000 --- a/canonical/canonical-block-executor/src/canonical_block_executor/sui_block_executor/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# `sui-block-executor` -**Note**: This may be worthy of its own crate--even outside of the canonical development altogether--as we plan to reuse it in the future. - -Sui does not have blocks by default. However, it is possible to use Sui's notion of checkpoints to create an execution model that is similar to blocks. Furthermore, we can disentangle this execution from consensus by using the checkpoint runtime by itself. This is the approach currently adopted in this module. \ No newline at end of file diff --git a/canonical/canonical-block-executor/src/canonical_block_executor/sui_block_executor/mod.rs b/canonical/canonical-block-executor/src/canonical_block_executor/sui_block_executor/mod.rs deleted file mode 100644 index 8d0044117..000000000 --- a/canonical/canonical-block-executor/src/canonical_block_executor/sui_block_executor/mod.rs +++ /dev/null @@ -1,235 +0,0 @@ -use core::fmt; -use std::sync::Arc; -use tracing::{debug, error, info, warn}; -use tokio::sync::RwLock; -use sui_core::{ - self, - transaction_manager::TransactionManager, - checkpoints::{ - checkpoint_executor::{CheckpointExecutor, self}, - CheckpointStore - }, - authority::{ - authority_per_epoch_store::AuthorityPerEpochStore, - AuthorityStore, - AuthorityState, - test_authority_builder::TestAuthorityBuilder - }, - state_accumulator::StateAccumulator, -}; -use sui_types::{ - messages_checkpoint::VerifiedCheckpoint, - transaction::{Transaction, VerifiedTransaction}, - executable_transaction::VerifiedExecutableTransaction -}; -use tokio::sync::broadcast::{self, Sender, Receiver}; -use sui_swarm_config::test_utils::CommitteeFixture; -use tempfile::tempdir; -use tracing_subscriber::field::debug; - -#[derive(Clone)] -pub struct SuiBlockExecutor { - pub authority_state : Arc, - pub checkpoint_executor : Arc>, - pub state_accumulator : Arc, - pub verified_checkpoint_sender : Sender, - pub committee_fixture : Arc, -} - -impl fmt::Debug for SuiBlockExecutor { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SuiBlockExecutor") - .finish() - } -} - -impl SuiBlockExecutor { - - pub fn new( - authority_state : Arc, - checkpoint_executor : Arc>, - state_accumulator : Arc, - verified_checkpoint_sender : Sender, - committee_fixture : Arc, - ) -> Self { - Self { - authority_state, - checkpoint_executor, - state_accumulator, - verified_checkpoint_sender, - committee_fixture, - } - } - - pub async fn init( - buffer_size : usize, - path : Option, - )-> Result { - - let checkpoint_store_path = match path { - Some(path_buf) => path_buf.as_path().to_owned(), - None => { - let dir = tempdir()?; - dir.path().to_owned() - }, - }; - - let store = CheckpointStore::new(&checkpoint_store_path); - - let network_config = - sui_swarm_config::network_config_builder::ConfigBuilder::new_with_temp_dir().build(); - let state = TestAuthorityBuilder::new() - .with_network_config(&network_config) - .build() - .await; - - let (checkpoint_sender, _): (Sender, Receiver) = - broadcast::channel(buffer_size); - - let accumulator = StateAccumulator::new(state.database.clone()); - let accumulator = Arc::new(accumulator); - - let executor = CheckpointExecutor::new_for_tests( - checkpoint_sender.subscribe(), - store.clone(), - state.database.clone(), - state.transaction_manager().clone(), - accumulator.clone(), - ); - - Ok(Self::new( - state, - Arc::new(RwLock::new(executor)), - accumulator, - checkpoint_sender, - Arc::new(CommitteeFixture::from_network_config(&network_config)), - )) - - } - - pub async fn execute_block(&self, block : Vec)-> Result<(), anyhow::Error> { - - let epoch_store = { - let state = self.authority_state.clone(); - - // Enqueue transactions in the transaction manager - let transaction_manager: Arc = state.transaction_manager().clone(); - let epoch_store = state.epoch_store_for_testing().to_owned(); - debug!("Enqueueing transactions..."); - transaction_manager.enqueue(block, &epoch_store)?; - debug!("Enqueued transactions"); - epoch_store - }; - - // Run the epoch - // todo: confirm running the epoch waits for all transactions to be complete - { - let mut checkpoint_executor = self.checkpoint_executor.write().await; - checkpoint_executor.run_epoch(epoch_store).await; - - } - - Ok(()) - - } - -} - -#[cfg(test)] -mod test { - - use super::*; - use tracing::{debug, error, info, warn}; - use tracing_subscriber::{FmtSubscriber, EnvFilter}; - - use sui_test_transaction_builder::TestTransactionBuilder; - - use sui_types::{ - base_types::ObjectID, - crypto::deterministic_random_account_key, - digests::TransactionEffectsDigest, - object::Object, - storage::InputKey, - transaction::{CallArg, ObjectArg}, - SUI_FRAMEWORK_PACKAGE_ID, - }; - - #[ctor::ctor] - fn before_all() { - // Create a filter based on the RUST_LOG environment variable - let filter = EnvFilter::from_default_env(); - - // Create a FmtSubscriber with the filter - let subscriber = FmtSubscriber::builder() - .with_env_filter(filter) - .finish(); - - // Set the subscriber as the global default - tracing::subscriber::set_global_default(subscriber).expect("Failed to set the global tracing subscriber"); - } - - fn make_transaction(gas_object: Object, input: Vec) -> VerifiedExecutableTransaction { - // Use fake module, function, package and gas prices since they are irrelevant for testing - // transaction manager. - let rgp = 100; - let (sender, keypair) = deterministic_random_account_key(); - let transaction = - TestTransactionBuilder::new(sender, gas_object.compute_object_reference(), rgp) - .move_call(SUI_FRAMEWORK_PACKAGE_ID, "counter", "assert_value", input) - .build_and_sign(&keypair); - VerifiedExecutableTransaction::new_system(VerifiedTransaction::new_unchecked(transaction), 0) - } - - #[tokio::test] - async fn test_empty_block() { - - let executor = super::SuiBlockExecutor::init(10, None).await.unwrap(); - - let block = vec![]; - - executor.execute_block(block).await.unwrap(); - - } - - #[tokio::test] - async fn test_genesis_transaction() { - - let (owner, _keypair) = deterministic_random_account_key(); - - let executor = super::SuiBlockExecutor::init(10, None).await.unwrap(); - - let transaction = VerifiedExecutableTransaction::new_system( - VerifiedTransaction::new_genesis_transaction(vec![]), - 0 - ); - let block = vec![transaction]; - - executor.execute_block(block).await.unwrap(); - } - - - #[tokio::test] - async fn test_single_transaction() { - - /*let (owner, _keypair) = deterministic_random_account_key(); - let gas_objects: Vec = (0..10) - .map(|_| { - let gas_object_id = ObjectID::random(); - Object::with_id_owner_for_testing(gas_object_id, owner) - }) - .collect(); - - let executor = super::SuiBlockExecutor::init(10, None).await.unwrap(); - - let genesis_transaction = VerifiedExecutableTransaction::new_system( - VerifiedTransaction::new_genesis_transaction(gas_objects.clone().), - 0 - ); - let transaction = make_transaction(gas_objects[0].clone(), vec![]); - let block = vec![transaction]; - - executor.execute_block(block).await.unwrap();*/ - - } - -} \ No newline at end of file diff --git a/canonical/canonical-block-executor/src/lib.rs b/canonical/canonical-block-executor/src/lib.rs deleted file mode 100644 index e0f3abad3..000000000 --- a/canonical/canonical-block-executor/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod canonical_block_executor; \ No newline at end of file diff --git a/canonical/canonical-move-natives/Cargo.toml b/canonical/canonical-move-natives/Cargo.toml deleted file mode 100644 index e313416ae..000000000 --- a/canonical/canonical-move-natives/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -[package] -name = "canonical-move-natives" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -avalanche-types = { version = "0.0.398", features = ["subnet", "codec_base64"] } -tokio = { version = "1.25.0", features = ["fs", "rt-multi-thread"] } -tonic = { version = "0.8.3", features = ["gzip"] } -serde = { version = "1.0.152", features = ["derive"] } -serde_json = "1.0.93" # https://github.com/serde-rs/json/releases -serde_with = { version = "2.2.0", features = ["hex"] } -log = "0.4.17" -dirs = "5.0.1" -hex = "0.4.3" -bytes = "1.4.0" -env_logger = "0.10.0" -base64 = { version = "0.21.0" } -chrono = "0.4.23" -derivative = "2.2.0" -jsonrpc-core = "18.0.0" -jsonrpc-core-client = { version = "18.0.0" } -jsonrpc-derive = "18.0.0" -async-channel = "1.9.0" - -anyhow = { workspace = true } -futures = { workspace = true } -rand = { workspace = true } -bcs = { workspace = true } -movement-sdk = { workspace = true } - -# sui -# todo: conflicting rocksdb means we can't use workspace -# todo: likely movement-sdk will move into its own workspace -# todo: once that happens, we can move sui into its own workspace -# todo: we will have to reconcile the two when we begin on the canonical VM -sui-adapter-latest = { workspace = true } -sui-types = { workspace = true } diff --git a/canonical/canonical-move-natives/README.md b/canonical/canonical-move-natives/README.md deleted file mode 100644 index 995c67b8a..000000000 --- a/canonical/canonical-move-natives/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# `canonical-move-natives` -This crate is used to create the move function tables for both the `AptosVm` and `SuiExecutor` by wrapping over the `CanonicalMoveResolver`. - -In both cases, the natives table SHALL consist of -- Move stdlib natives (automatically compatible). -- Aptos natives (needs investigation). -- Sui natives (should be compatible via the resolver). \ No newline at end of file diff --git a/canonical/canonical-move-natives/src/canonical_move_natives/mod.rs b/canonical/canonical-move-natives/src/canonical_move_natives/mod.rs deleted file mode 100644 index e69de29bb..000000000 diff --git a/canonical/canonical-move-natives/src/lib.rs b/canonical/canonical-move-natives/src/lib.rs deleted file mode 100644 index e69de29bb..000000000 diff --git a/canonical/canonical-move-resolver/Cargo.toml b/canonical/canonical-move-resolver/Cargo.toml deleted file mode 100644 index 3cab1a044..000000000 --- a/canonical/canonical-move-resolver/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -name = "canonical-move-resolver" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -avalanche-types = { version = "0.0.398", features = ["subnet", "codec_base64"] } -tokio = { version = "1.25.0", features = ["fs", "rt-multi-thread"] } -tonic = { version = "0.8.3", features = ["gzip"] } -serde = { version = "1.0.152", features = ["derive"] } -serde_json = "1.0.93" # https://github.com/serde-rs/json/releases -serde_with = { version = "2.2.0", features = ["hex"] } -log = "0.4.17" -dirs = "5.0.1" -hex = "0.4.3" -bytes = "1.4.0" -env_logger = "0.10.0" -base64 = { version = "0.21.0" } -chrono = "0.4.23" -derivative = "2.2.0" -jsonrpc-core = "18.0.0" -jsonrpc-core-client = { version = "18.0.0" } -jsonrpc-derive = "18.0.0" -async-channel = "1.9.0" - -anyhow = { workspace = true } -futures = { workspace = true } -rand = { workspace = true } -bcs = { workspace = true } -movement-sdk = { workspace = true } - -# aptos -aptos-storage-interface = { workspace = true } - -# sui -# todo: conflicting rocksdb means we can't use workspace -# todo: likely movement-sdk will move into its own workspace -# todo: once that happens, we can move sui into its own workspace -# todo: we will have to reconcile the two when we begin on the canonical VM -sui-adapter-latest = { workspace = true } -sui-types = { workspace = true } diff --git a/canonical/canonical-move-resolver/README.md b/canonical/canonical-move-resolver/README.md deleted file mode 100644 index 6dc555f52..000000000 --- a/canonical/canonical-move-resolver/README.md +++ /dev/null @@ -1 +0,0 @@ -# Canonical VM \ No newline at end of file diff --git a/canonical/canonical-move-resolver/src/canonical_move_resolver/aptos.rs b/canonical/canonical-move-resolver/src/canonical_move_resolver/aptos.rs deleted file mode 100644 index d3c89d8ba..000000000 --- a/canonical/canonical-move-resolver/src/canonical_move_resolver/aptos.rs +++ /dev/null @@ -1,5 +0,0 @@ -/// An easy way to do this is actually to wrap over the reader-writer, -/// rather than directly implementing the move resolver. -mod db_reader_writer { - -} \ No newline at end of file diff --git a/canonical/canonical-move-resolver/src/canonical_move_resolver/mod.rs b/canonical/canonical-move-resolver/src/canonical_move_resolver/mod.rs deleted file mode 100644 index 51c58957f..000000000 --- a/canonical/canonical-move-resolver/src/canonical_move_resolver/mod.rs +++ /dev/null @@ -1,30 +0,0 @@ -pub mod aptos; -pub mod sui; - -/// This is where the common resolver should be defined. -/// Essentially, it will be composed of a Sui resolver and an Aptos resolver. -/// First, we will check for an Aptos struct. -/// Then, we will check for a Sui object. - -// todo: skeleton -pub struct CommonResolver<'state> { - aptos_resolver : AptosResolver<'state>, - sui_resolver : SuiResolver<'state> -} - -// todo: skeleton -impl <'state>ResourceResolver for CommonResolver<'state> { - - fn get_resource() { - - // aptos resolver takes priority - match self.aptos_resolver.get_resource() { - Some(resource) => Some(resource), - None=>{ - self.sui_resolver.get_resource() - } - } - - } - -} \ No newline at end of file diff --git a/canonical/canonical-move-resolver/src/canonical_move_resolver/sui.rs b/canonical/canonical-move-resolver/src/canonical_move_resolver/sui.rs deleted file mode 100644 index 71ef8ef01..000000000 --- a/canonical/canonical-move-resolver/src/canonical_move_resolver/sui.rs +++ /dev/null @@ -1,3 +0,0 @@ -/*impl Into for { - -}*/ \ No newline at end of file diff --git a/canonical/canonical-move-resolver/src/lib.rs b/canonical/canonical-move-resolver/src/lib.rs deleted file mode 100644 index 729916a6c..000000000 --- a/canonical/canonical-move-resolver/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod util; -pub mod canonical_move_resolver; \ No newline at end of file diff --git a/canonical/canonical-move-resolver/src/main.rs b/canonical/canonical-move-resolver/src/main.rs deleted file mode 100644 index d83a47485..000000000 --- a/canonical/canonical-move-resolver/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub fn main() { - unimplemented!() -} \ No newline at end of file diff --git a/canonical/canonical-move-resolver/src/util/mod.rs b/canonical/canonical-move-resolver/src/util/mod.rs deleted file mode 100644 index e69de29bb..000000000 diff --git a/canonical/canonical-rpc/Cargo.toml b/canonical/canonical-rpc/Cargo.toml deleted file mode 100644 index 4d5d13620..000000000 --- a/canonical/canonical-rpc/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -[package] -name = "canonical-rpc" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -avalanche-types = { version = "0.0.398", features = ["subnet", "codec_base64"] } -tokio = { version = "1.25.0", features = ["fs", "rt-multi-thread"] } -tonic = { version = "0.8.3", features = ["gzip"] } -serde = { version = "1.0.152", features = ["derive"] } -serde_json = "1.0.93" # https://github.com/serde-rs/json/releases -serde_with = { version = "2.2.0", features = ["hex"] } -log = "0.4.17" -dirs = "5.0.1" -hex = "0.4.3" -bytes = "1.4.0" -env_logger = "0.10.0" -base64 = { version = "0.21.0" } -chrono = "0.4.23" -derivative = "2.2.0" -jsonrpc-core = "18.0.0" -jsonrpc-core-client = { version = "18.0.0" } -jsonrpc-derive = "18.0.0" -async-channel = "1.9.0" - -anyhow = { workspace = true } -futures = { workspace = true } -rand = { workspace = true } -bcs = { workspace = true } -movement-sdk = { workspace = true } - -# sui -# todo: conflicting rocksdb means we can't use workspace -# todo: likely movement-sdk will move into its own workspace -# todo: once that happens, we can move sui into its own workspace -# todo: we will have to reconcile the two when we begin on the canonical VM -sui-adapter-latest = { workspace = true } -sui-types = { workspace = true } diff --git a/canonical/canonical-rpc/README.md b/canonical/canonical-rpc/README.md deleted file mode 100644 index b6f9785a9..000000000 --- a/canonical/canonical-rpc/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# `canonical-rpc` -The canonical RPC is the complete RPC implementation for the Canonical Move VM. It contains: -- `canonical/` routes which are routes supporting the combined execution of Aptos and Sui. -- `aptos/` routes which are compatible with the upstream Aptos RPC. -- `sui/` routes which are compatible with the upstream Sui RPC. -- `movement/` routes which are derived as a consequence of implementing within the `movement-sdk` framework. \ No newline at end of file diff --git a/canonical/canonical-rpc/src/lib.rs b/canonical/canonical-rpc/src/lib.rs deleted file mode 100644 index e69de29bb..000000000 diff --git a/canonical/canonical-types/Cargo.toml b/canonical/canonical-types/Cargo.toml deleted file mode 100644 index a0bb537b1..000000000 --- a/canonical/canonical-types/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "standalone-sui-subnet" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -avalanche-types = { version = "0.0.398", features = ["subnet", "codec_base64"] } -tokio = { version = "1.25.0", features = ["fs", "rt-multi-thread"] } -tonic = { version = "0.8.3", features = ["gzip"] } -serde = { version = "1.0.152", features = ["derive"] } -serde_json = "1.0.93" # https://github.com/serde-rs/json/releases -serde_with = { version = "2.2.0", features = ["hex"] } -log = "0.4.17" -dirs = "5.0.1" -hex = "0.4.3" -bytes = "1.4.0" -env_logger = "0.10.0" -base64 = { version = "0.21.0" } -chrono = "0.4.23" -derivative = "2.2.0" -jsonrpc-core = "18.0.0" -jsonrpc-core-client = { version = "18.0.0" } -jsonrpc-derive = "18.0.0" -async-channel = "1.9.0" - -anyhow = { workspace = true } -futures = { workspace = true } -rand = { workspace = true } -bcs = { workspace = true } -movement-sdk = { workspace = true } - -# sui -sui-adapter-latest = { workspace = true } -sui-types = { workspace = true } - -aptos-types = { workspace = true} diff --git a/canonical/canonical-types/src/block/block.rs b/canonical/canonical-types/src/block/block.rs deleted file mode 100644 index 943380a06..000000000 --- a/canonical/canonical-types/src/block/block.rs +++ /dev/null @@ -1,51 +0,0 @@ -// todo: reduce import depth -use crate::transaction::transaction::Transaction; -use aptos_types::transaction::Transaction as AptosTransaction; - -#[derive(Clone, Debug)] -pub struct Block(Vec); - -impl Block { - - pub fn new(transactions: Vec) -> Self { - Block(transactions) - } - - /// Filters transactions to those enums which contain Aptos transactions. - pub fn filter_aptos_transactions(&self) -> Vec { - self.0.iter().filter(|t| t.is_aptos()).cloned().collect() - } - - /// Extracts Aptos transactions refs from the block. - pub fn extract_aptos_transaction_refs(&self) -> Vec<&AptosTransaction> { - self.0.iter().filter_map(|transactions| { - match transactions { - Transaction::Aptos(aptos_transaction) => Some(aptos_transaction), - _ => None - } - }).collect() - } - - /// Extracts Aptos transactions from the block. - pub fn extract_aptos_transactions(&self) -> Vec { - self.0.iter().filter_map(|transactions| { - match transactions { - Transaction::Aptos(aptos_transaction) => Some(aptos_transaction.clone()), - _ => None - } - }).collect() - } - -} - -impl Into> for Block { - fn into(self) -> Vec { - self.0 - } -} - -impl Into for Vec { - fn into(self) -> Block { - Block(self) - } -} \ No newline at end of file diff --git a/canonical/canonical-types/src/block/mod.rs b/canonical/canonical-types/src/block/mod.rs deleted file mode 100644 index 49bc7d4bf..000000000 --- a/canonical/canonical-types/src/block/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod block; \ No newline at end of file diff --git a/canonical/canonical-types/src/lib.rs b/canonical/canonical-types/src/lib.rs deleted file mode 100644 index cc8af46d5..000000000 --- a/canonical/canonical-types/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod transaction; -pub mod block; \ No newline at end of file diff --git a/canonical/canonical-types/src/transaction/mod.rs b/canonical/canonical-types/src/transaction/mod.rs deleted file mode 100644 index be9dda049..000000000 --- a/canonical/canonical-types/src/transaction/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod transaction; \ No newline at end of file diff --git a/canonical/canonical-types/src/transaction/transaction.rs b/canonical/canonical-types/src/transaction/transaction.rs deleted file mode 100644 index a99bbff1d..000000000 --- a/canonical/canonical-types/src/transaction/transaction.rs +++ /dev/null @@ -1,28 +0,0 @@ -use aptos_types::transaction::{Transaction as AptosTransaction}; -use sui_types::transaction::{Transaction as SuiTransaction}; - -#[derive(Clone, Debug)] -pub enum Transaction { - Aptos(AptosTransaction), - Sui(SuiTransaction) -} - -impl Transaction { - - - - pub fn is_aptos(&self) -> bool { - match self { - Transaction::Aptos(_) => true, - Transaction::Sui(_) => false - } - } - - pub fn is_sui(&self) -> bool { - match self { - Transaction::Aptos(_) => false, - Transaction::Sui(_) => true - } - } - -} diff --git a/canonical/rsc/canonical_shared_resolver.png b/canonical/rsc/canonical_shared_resolver.png deleted file mode 100644 index 3e52c696cc7998b1c25848f01a8d5edc68e4b99a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108418 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 H&e8uLUNPDa diff --git a/canonical/rsc/sui_block_executor.png b/canonical/rsc/sui_block_executor.png deleted file mode 100644 index e3eec52213ae21c9a166ed7264e1fc5e12df92a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80085 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 Q{mVgd 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 diff --git a/canonical/rust-toolchain b/canonical/rust-toolchain deleted file mode 100644 index 832e9afb6..000000000 --- a/canonical/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -1.70.0 diff --git a/canonical/rustfmt.toml b/canonical/rustfmt.toml deleted file mode 100644 index f1c193bbc..000000000 --- a/canonical/rustfmt.toml +++ /dev/null @@ -1,11 +0,0 @@ -combine_control_expr = false -edition = "2021" -imports_granularity = "Crate" -format_macro_matchers = true -group_imports = "One" -hex_literal_case = "Upper" -match_block_trailing_comma = true -newline_style = "Unix" -overflow_delimited_expr = true -reorder_impl_items = true -use_field_init_shorthand = true From 6438553442063c69c5ad68fdd2308c64dad77553 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 7 Dec 2023 07:27:23 -1000 Subject: [PATCH 57/58] chore: scaffolding and docs. --- movement-sdk/Cargo.lock | 65 ++++++++++++- movement-sdk/Cargo.toml | 15 +++ .../execution/aptos-block-executor/Cargo.toml | 21 ++++ .../src/aptos_block_executor.rs | 95 +++++++++++++++++++ .../execution/aptos-block-executor/src/lib.rs | 1 + .../canonical-block-executor/Cargo.toml | 16 ++++ .../src/canonical_block_executor.rs | 0 .../canonical-block-executor/src/lib.rs | 0 .../execution/sui-block-executor/Cargo.toml | 4 +- .../execution/sui-block-executor/README.md | 12 ++- .../src/sui_block_executor.rs | 59 ++++++++---- .../sui-block-authority-providers/Cargo.toml | 4 +- .../sui-block-authority-providers/src/lib.rs | 1 + .../src/object_version/README.md | 21 ++++ .../src/object_version/mod.rs | 1 + .../object_version/object_version_rocksdb.rs | 21 ++++ .../sui-block-authority-rpc/README.md | 0 .../types/aptos-helper-types/Cargo.toml | 17 ++++ .../types/aptos-helper-types/README.md | 2 + .../aptos-helper-types/src/block/block.rs | 11 +++ .../types/aptos-helper-types/src/block/mod.rs | 2 + .../types/aptos-helper-types/src/lib.rs | 1 + movement-sdk/types/canonical-types/Cargo.toml | 33 +------ .../types/canonical-types/src/block/block.rs | 64 +++++++++---- .../src/transaction/transaction.rs | 2 +- movement-sdk/types/sui-helper-types/README.md | 2 + .../sui-helper-types/src/block/README.md | 41 ++++++++ .../types/sui-helper-types/src/block/block.rs | 21 +++- .../src/providers/gas_info.rs | 2 +- .../sui-helper-types/src/providers/mod.rs | 2 +- .../src/providers/object_version.rs | 10 ++ .../src/providers/object_version_provider.rs | 8 -- 32 files changed, 465 insertions(+), 89 deletions(-) create mode 100644 movement-sdk/execution/aptos-block-executor/Cargo.toml create mode 100644 movement-sdk/execution/aptos-block-executor/src/aptos_block_executor.rs create mode 100644 movement-sdk/execution/aptos-block-executor/src/lib.rs create mode 100644 movement-sdk/execution/canonical-block-executor/Cargo.toml create mode 100644 movement-sdk/execution/canonical-block-executor/src/canonical_block_executor.rs create mode 100644 movement-sdk/execution/canonical-block-executor/src/lib.rs create mode 100644 movement-sdk/sui-helpers/sui-block-authority-providers/src/object_version/README.md create mode 100644 movement-sdk/sui-helpers/sui-block-authority-providers/src/object_version/mod.rs create mode 100644 movement-sdk/sui-helpers/sui-block-authority-providers/src/object_version/object_version_rocksdb.rs create mode 100644 movement-sdk/sui-helpers/sui-block-authority-rpc/README.md create mode 100644 movement-sdk/types/aptos-helper-types/Cargo.toml create mode 100644 movement-sdk/types/aptos-helper-types/README.md create mode 100644 movement-sdk/types/aptos-helper-types/src/block/block.rs create mode 100644 movement-sdk/types/aptos-helper-types/src/block/mod.rs create mode 100644 movement-sdk/types/aptos-helper-types/src/lib.rs create mode 100644 movement-sdk/types/sui-helper-types/README.md create mode 100644 movement-sdk/types/sui-helper-types/src/block/README.md create mode 100644 movement-sdk/types/sui-helper-types/src/providers/object_version.rs delete mode 100644 movement-sdk/types/sui-helper-types/src/providers/object_version_provider.rs diff --git a/movement-sdk/Cargo.lock b/movement-sdk/Cargo.lock index abeaea067..de4052a50 100644 --- a/movement-sdk/Cargo.lock +++ b/movement-sdk/Cargo.lock @@ -535,6 +535,20 @@ dependencies = [ "rayon", ] +[[package]] +name = "aptos-block-executor" +version = "0.1.0" +dependencies = [ + "anyhow", + "aptos-executor", + "aptos-helper-types", + "aptos-types", + "aptos-vm", + "async-trait", + "movement-sdk", + "tokio", +] + [[package]] name = "aptos-block-partitioner" version = "0.1.0-canonical-aptos" @@ -1275,6 +1289,17 @@ dependencies = [ name = "aptos-global-constants" version = "0.1.0-canonical-aptos" +[[package]] +name = "aptos-helper-types" +version = "0.1.0" +dependencies = [ + "anyhow", + "aptos-crypto", + "aptos-types", + "async-trait", + "futures", +] + [[package]] name = "aptos-id-generator" version = "0.1.0-canonical-aptos" @@ -1382,7 +1407,7 @@ version = "0.1.0-canonical-aptos" dependencies = [ "anyhow", "aptos-bitvec", - "aptos-block-executor", + "aptos-block-executor 0.1.0-canonical-aptos", "aptos-cached-packages", "aptos-crypto", "aptos-framework", @@ -2469,7 +2494,7 @@ version = "0.1.0-canonical-aptos" dependencies = [ "anyhow", "aptos-aggregator", - "aptos-block-executor", + "aptos-block-executor 0.1.0-canonical-aptos", "aptos-block-partitioner", "aptos-crypto", "aptos-crypto-derive", @@ -4014,6 +4039,17 @@ version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +[[package]] +name = "canonical-block-executor" +version = "0.1.0" +dependencies = [ + "anyhow", + "aptos-helper-types", + "async-trait", + "movement-sdk", + "tokio", +] + [[package]] name = "captcha" version = "0.0.9" @@ -12665,6 +12701,17 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "standalone-sui-subnet" +version = "0.1.0" +dependencies = [ + "aptos-crypto", + "aptos-helper-types", + "aptos-types", + "sui-helper-types", + "sui-types", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -12930,11 +12977,23 @@ dependencies = [ ] [[package]] -name = "sui-block" +name = "sui-block-authority-providers" version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "futures", + "sui-helper-types", + "sui-types", +] + +[[package]] +name = "sui-block-executor" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", "movement-sdk", "sui-helper-types", "sui-types", diff --git a/movement-sdk/Cargo.toml b/movement-sdk/Cargo.toml index 8fbefbe1b..4b6028ffb 100644 --- a/movement-sdk/Cargo.toml +++ b/movement-sdk/Cargo.toml @@ -7,9 +7,16 @@ members = [ # execution "execution/sui-block-executor", + "execution/aptos-block-executor", + "execution/canonical-block-executor", # types "types/sui-helper-types", + "types/canonical-types", + "types/aptos-helper-types", + + # sui helpers + "sui-helpers/sui-block-authority-providers", # clis "clis/movement" @@ -28,7 +35,14 @@ rust-version = "1.70" [workspace.dependencies] # internal movement-sdk = { path = "movement-sdk" } +aptos-helper-types = { path = "types/aptos-helper-types" } sui-helper-types = { path = "types/sui-helper-types" } +canonical-types = { path = "types/canonical-types" } +sui-block-executor = { path = "execution/sui-block-executor" } +aptos-block-executor = { path = "execution/aptos-block-executor" } +canonical-block-executor = { path = "execution/canonical-block-executor" } +sui-block-authority-providers = { path = "sui-helpers/sui-block-authority-providers" } + async-trait = { version = "0.1" } @@ -64,6 +78,7 @@ aptos-types = { path = "../vendors/aptos-core/types" } aptos-executor = { path = "../vendors/aptos-core/execution/executor" } aptos-executor-types = { path = "../vendors/aptos-core/execution/executor-types" } aptos-storage-interface = { path = "../vendors/aptos-core/storage/storage-interface" } +aptos-crypto = { path = "../vendors/aptos-core/crates/aptos-crypto" } # sui sui-adapter-latest = { path = "../vendors/sui/sui-execution/latest/sui-adapter" } diff --git a/movement-sdk/execution/aptos-block-executor/Cargo.toml b/movement-sdk/execution/aptos-block-executor/Cargo.toml new file mode 100644 index 000000000..6b715d615 --- /dev/null +++ b/movement-sdk/execution/aptos-block-executor/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "aptos-block-executor" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# internal +movement-sdk = { workspace = true } + +# general +anyhow = { workspace = true } +async-trait = { workspace = true } +tokio = { workspace = true } + +# aptos +aptos-executor = { workspace = true } +aptos-types = { workspace = true } +aptos-vm = { workspace = true } +aptos-helper-types = { workspace = true } \ No newline at end of file diff --git a/movement-sdk/execution/aptos-block-executor/src/aptos_block_executor.rs b/movement-sdk/execution/aptos-block-executor/src/aptos_block_executor.rs new file mode 100644 index 000000000..e53f151fd --- /dev/null +++ b/movement-sdk/execution/aptos-block-executor/src/aptos_block_executor.rs @@ -0,0 +1,95 @@ +use tokio::sync::RwLock; +use std::sync::Arc; +use aptos_executor::block_executor::BlockExecutor; +use aptos_types::transaction::Transaction; +use aptos_vm::AptosVM; +use movement_sdk::{ExecutionLayer, Layer}; +use aptos_helper_types::block::Block; + + +#[derive(Clone)] +pub struct AptosBlockExecutor { + pub executor: Arc>>, +} + +impl std::fmt::Debug for AptosBlockExecutor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("AptosBlockExecutor") + .finish() + } +} + +impl AptosBlockExecutor { + pub fn new(executor: Arc>>) -> Self { + AptosBlockExecutor { executor } + } +} + +impl Layer for AptosBlockExecutor {} + +#[async_trait::async_trait] +impl ExecutionLayer for AptosBlockExecutor { + + type Block = Block; + type BlockId = String; // todo: change later + type ChangeSet = u64; // todo: change later + + // Gets the next block from the previous layer. + async fn get_next_block( + &self + ) -> Result, anyhow::Error> { + unimplemented!(); + } + + // Executes a block and produces a change set. + async fn execute_block( + &self, + block: Self::Block + ) -> Result { + + let mut executor = self.executor.write().await; + // let parent_block_id_now = executor.committed_block_id(); + + // todo: update for aptos 1.70 + /*// execute the block + let output = executor + .execute_block((block_id, block_tx.clone()), parent_block_id) + .unwrap(); + + // sign for the the ledger + let ledger_info = LedgerInfo::new( + BlockInfo::new( + next_epoch, + 0, + block_id, + output.root_hash(), + output.version(), + ts, + output.epoch_state().clone(), + ), + HashValue::zero(), + ); + let li = generate_ledger_info_with_sig(&[self.signer.as_ref().unwrap().clone()], ledger_info); + executor.commit_blocks(vec![block_id], li.clone()).unwrap();*/ + + Ok(0) + + } + + // Sends a change set to the next layer, i.e., the storage layer. + async fn send_change_set( + &self, + change_set: Self::ChangeSet + ) -> Result<(), anyhow::Error> { + unimplemented!(); + } + + // Gets an executed block + async fn get_block( + &self, + block_id: Self::BlockId + ) -> Result, anyhow::Error> { + unimplemented!(); + } + +} diff --git a/movement-sdk/execution/aptos-block-executor/src/lib.rs b/movement-sdk/execution/aptos-block-executor/src/lib.rs new file mode 100644 index 000000000..c2acc8c4c --- /dev/null +++ b/movement-sdk/execution/aptos-block-executor/src/lib.rs @@ -0,0 +1 @@ +pub mod aptos_block_executor; \ No newline at end of file diff --git a/movement-sdk/execution/canonical-block-executor/Cargo.toml b/movement-sdk/execution/canonical-block-executor/Cargo.toml new file mode 100644 index 000000000..204b23705 --- /dev/null +++ b/movement-sdk/execution/canonical-block-executor/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "canonical-block-executor" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# internal +movement-sdk = { workspace = true } + +# general +anyhow = { workspace = true } +async-trait = { workspace = true } +tokio = { workspace = true } +aptos-helper-types = { workspace = true } diff --git a/movement-sdk/execution/canonical-block-executor/src/canonical_block_executor.rs b/movement-sdk/execution/canonical-block-executor/src/canonical_block_executor.rs new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/execution/canonical-block-executor/src/lib.rs b/movement-sdk/execution/canonical-block-executor/src/lib.rs new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/execution/sui-block-executor/Cargo.toml b/movement-sdk/execution/sui-block-executor/Cargo.toml index 304f9ce50..f9ba60248 100644 --- a/movement-sdk/execution/sui-block-executor/Cargo.toml +++ b/movement-sdk/execution/sui-block-executor/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "sui-block" +name = "sui-block-executor" version = "0.1.0" edition = "2021" @@ -13,11 +13,13 @@ movement-sdk = { workspace = true } anyhow = { workspace = true } async-trait = { workspace = true } tokio = { workspace = true } +futures = { workspace = true } # internal sui-helper-types = { workspace = true } sui-types = { workspace = true } + # sui # todo: conflicting rocksdb means we can't use workspace # todo: likely movement-sdk will move into its own workspace diff --git a/movement-sdk/execution/sui-block-executor/README.md b/movement-sdk/execution/sui-block-executor/README.md index baea5d103..8491e1067 100644 --- a/movement-sdk/execution/sui-block-executor/README.md +++ b/movement-sdk/execution/sui-block-executor/README.md @@ -5,7 +5,7 @@ The `sui-block-executor` is a block-based execution layer providing Move VM exec Essentially, the `sui-block-executor` performs the duty of... 1. Transforming [`Vec`](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-types/src/transaction.rs#L2019) into a [`Vec`](https://github.com/MystenLabs/sui/blob/main/crates/sui-types/src/executable_transaction.rs#L55), and then, 2. Executing each element in said vector in deterministic order via [`execute_transaction_to_effects`](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/sui-execution/src/latest.rs#L79). -3. `execute_transaction_to_effects` both returns the `TransactionEffects` and applies them to the backing store which the `AuthorityStore` implements (by implementing the [required traits](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-types/src/storage/mod.rs#L489)). Take a look here to see for example how they are not used directly in the [`AuthorityStore`'s execution_driver](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/execution_driver.rs#L103). However, there's additional complexity here that is addressed below with which we are not yet sure whether we will need to deal. +3. `execute_transaction_to_effects` both returns the `TransactionEffects` and applies them to a temporary store within the `AuthorityStore` (which implements `BackingStore` via [required traits](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-types/src/storage/mod.rs#L489)). These effects must be committed--which is discussed further below. > Perhaps the most analogous path in the existing Sui source to what needs to be developed is the [`process_certificate` method from the `AuthorityStore`](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L1339). This is a good point of reference for many parts of this project, hence it is included in the reading material. @@ -48,7 +48,7 @@ fn execute_transaction_to_effects( ``` - `&dyn BackingStore`: arguably the most important of the parameters. We essentially want to do this over RocksDB on our own. There may be several crates for this developed over time, but `canonical-sui-backing-store` is where we will start. -- `protocol_config`: this is struct pops up all over the Sui crate and was originally a big part of the cause for hesitancy in implementing the Sui block-executor at this level--owing to the presumed entanglement of consensus in parts of the object runtime specifically attributable to this config. However, it turns out that consensus parameters are not in fact used. Instead, what this is [used for at this level](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/sui-execution/v1/sui-adapter/src/programmable_transactions/execution.rs#L639) are things such as... +- `protocol_config`: this is a struct that pops up all over the Sui crate and was originally a big part of the cause for hesitancy in implementing the Sui block-executor at this level--owing to the presumed entanglement of consensus in parts of the object runtime specifically attributable to this config. However, it turns out that consensus parameters are not in fact used. Instead, what this is [used for at this level](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/sui-execution/v1/sui-adapter/src/programmable_transactions/execution.rs#L639) are things such as... - shared object deletion settings, - Move binary format settings, - Max package size. @@ -68,7 +68,11 @@ fn execute_transaction_to_effects( ### Dealing with `TransactionEffects` The [`AuthorityPerEpochStore`](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority/authority_per_epoch_store.rs#L628) is really the engine of a lot of how Sui execution comes together for the Sui network. However, it unfortunately is also very entangled and asynchronous. -Though `TransactionEffects` are applied via calling `execute_transaction_to_effects` the `AuthorityPerEpochStore` relies on these effects to inform processes which [affect execution within and between epochs](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority/authority_per_epoch_store.rs#L1172). For the most part, it seems like we can ignore this as we are not concerned with consensus details. However, a second glance should be made at the `AuthorityPerEpochStore` whenever it appears something is not working correctly concerning issues related to the absence, presence, etc. of checkpoint and epoch indicators. +The effects are then asynchronously committed to the epoch store using the returned [`TransactionEffects`](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L1213) and later make their way into persistent state. This process involves a lot of interdependencies in Sui. However, we do not necessarily have to implement this directly. Take a look here to see for example how they are not used directly in the [`AuthorityStore`'s execution_driver](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/execution_driver.rs#L103). However, there's additional complexity here that is addressed below with which we are not yet sure whether we will need to deal. + +To accomplish this, we must build a subsystem [similar to](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L1213). However, ours can be synchronous. + +This is an ongoing area of research. ## Sui Object Runtime `execute_transaction_to_effects` takes care of the Sui object runtime provided, so long as you are constructing the executor with a MoveVM similar to that produced by [`new_move_vm`](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/sui-execution/src/latest.rs#L55C35-L55C35). In fact `new_move_vm` gives a good entry point for extending the function table, which should be enough for the appropriate integration. @@ -76,7 +80,7 @@ Though `TransactionEffects` are applied via calling `execute_transaction_to_effe ## Sui-like Concurrency We need to deterministically group transactions into execution groups that can be safely and maximally executed in parallel based on their sets of shared objects. -Obtaining shared objects can be accomplished via the transaction directly. +Obtaining shared objects can be accomplished via the [transaction data directly](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L963C78-L963C78). The simplest way to solve this problem is to brute-force through possible groupings in transaction order which is $O(n^2 * ||S||)$. However, practically the sizes of the sets will be much smaller than $||S||$ and the number of transactions in a block should be on the order of 100s. diff --git a/movement-sdk/execution/sui-block-executor/src/sui_block_executor.rs b/movement-sdk/execution/sui-block-executor/src/sui_block_executor.rs index 633e072d5..cebf8a85c 100644 --- a/movement-sdk/execution/sui-block-executor/src/sui_block_executor.rs +++ b/movement-sdk/execution/sui-block-executor/src/sui_block_executor.rs @@ -1,39 +1,54 @@ +use std::fmt::Debug; + use sui_helper_types::{ providers::{ gas_info::GasInfoProvider, input_object::InputObjectProvider, epoch::EpochProvider, verified_executable_transaction::VerifiedExecutableBlockProvider, - object_version_provider::ObjectVersionProvider + object_version::ObjectVersionProvider }, block::{Block, VerifiedExecutableBlock} }; -use movement_sdk::ExecutionLayer; +use movement_sdk::{Layer, ExecutionLayer}; use sui_types::storage::BackingStore; +use std::sync::Arc; +use sui_types::executable_transaction::VerifiedExecutableTransaction; /// Sui block executor struct. /// ? Feel free to change the ref types to whatever you want. -#[derive(Debug, Clone)] + +#[derive(Clone)] pub struct SuiBlockExecutor { - backing_store : Box, - epoch_provider : Box, - gas_info_provider : Box, - input_object_provider : Box, - verified_executable_block_provider : Box, - object_version_provider : Box + backing_store : Arc, + epoch_provider : Arc, + gas_info_provider : Arc, + input_object_provider : Arc, + verified_executable_block_provider : Arc, + object_version_provider : Arc +} + + +impl Debug for SuiBlockExecutor { + + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SuiBlockExecutor") + .finish() + } + } impl SuiBlockExecutor { /// Creates a new sui block executor. pub fn new( - backing_store : Box, - epoch_provider : Box, - gas_info_provider : Box, - input_object_provider : Box, - verified_executable_block_provider : Box, - object_version_provider : Box + backing_store : Arc, + epoch_provider : Arc, + gas_info_provider : Arc, + input_object_provider : Arc, + verified_executable_block_provider : Arc, + object_version_provider : Arc ) -> Self { Self { backing_store, @@ -47,7 +62,7 @@ impl SuiBlockExecutor { async fn execute_transaction_group( &self, - transaction_group : Vec + transaction_group : Vec ) -> Result<(), anyhow::Error> { for transaction in transaction_group { // todo: use execute_transaction_to_effects @@ -57,6 +72,10 @@ impl SuiBlockExecutor { } +impl Layer for SuiBlockExecutor { + +} + #[async_trait::async_trait] impl ExecutionLayer for SuiBlockExecutor { @@ -81,15 +100,15 @@ impl ExecutionLayer for SuiBlockExecutor { let verified_executable_block = self.verified_executable_block_provider.verified_executable_block(&block).await?; // get the max parallel groups - let max_parallel_groups = verified_executable_block.get_max_parrallel_groups(); + let max_parallel_groups = verified_executable_block.get_max_parallel_groups(); // set up the object versions for the transactions - let sequencer_parallel_groups = self.object_version_provider.sequence_objects_for_transactions(max_parallel_groups).await?; + let sequencer_parallel_groups = self.object_version_provider.assign_shared_object_versions(max_parallel_groups).await?; // execute the transaction groups in parallel - futures::future::try_join_all!( + futures::future::try_join_all( sequencer_parallel_groups.into_iter().map(|transaction_group| self.execute_transaction_group(transaction_group)) - )?; + ).await?; unimplemented!(); // ! Worry about this for now. diff --git a/movement-sdk/sui-helpers/sui-block-authority-providers/Cargo.toml b/movement-sdk/sui-helpers/sui-block-authority-providers/Cargo.toml index 1220781b0..f29413d37 100644 --- a/movement-sdk/sui-helpers/sui-block-authority-providers/Cargo.toml +++ b/movement-sdk/sui-helpers/sui-block-authority-providers/Cargo.toml @@ -1,11 +1,13 @@ [package] -name = "sui-helper-types" +name = "sui-block-authority-providers" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +# internal +sui-helper-types = { workspace = true } # sui sui-types = { workspace = true } diff --git a/movement-sdk/sui-helpers/sui-block-authority-providers/src/lib.rs b/movement-sdk/sui-helpers/sui-block-authority-providers/src/lib.rs index e69de29bb..71198edda 100644 --- a/movement-sdk/sui-helpers/sui-block-authority-providers/src/lib.rs +++ b/movement-sdk/sui-helpers/sui-block-authority-providers/src/lib.rs @@ -0,0 +1 @@ +pub mod object_version; \ No newline at end of file diff --git a/movement-sdk/sui-helpers/sui-block-authority-providers/src/object_version/README.md b/movement-sdk/sui-helpers/sui-block-authority-providers/src/object_version/README.md new file mode 100644 index 000000000..4804cd1e3 --- /dev/null +++ b/movement-sdk/sui-helpers/sui-block-authority-providers/src/object_version/README.md @@ -0,0 +1,21 @@ +# `object_version` Providers +Sui execution relies on assigning sequence numbers to shared objects and shared object with sequence numbers to transactions. This is how it keeps determinism when executing. + +These are tracked in the tables shown [here] +(https://github.com/MystenLabs/sui/blob/6ec723bcbdc4c36358d444cbfcd88ae1378761a5/crates/sui-core/src/authority/authority_per_epoch_store.rs#L301). + +We would like to implement similar functionality to give ourselves valid sequence numbers for shared objects and transactions that will operate appropriately under concurrency. + +To this, we would like to implement the below... + +```rust +async fn assign_shared_object_versions(&self, transactions : VerifiedExecutableExecutionGroups) -> Result { + unimplemented!(); +} +``` + +This method takes one of our blocks that has been grouped for parallelism, `VerifiedExecutableExecutionGroups`, and assigns the sequence numbers and transactions in the persisted store according to how that group would be executed. + +That is to say, if one of the groups acts on a particular object in three transactions, the sequence numbers assigned to those versions of the object should be 1, 2, and 3--assuming the sequence number was originally 0. The object versions with those sequence numbers should also be assigned to the transactions. Those numbers are persisted and the next time a block executes, the sequence numbers should be 4, 5, and so on. + +We would like to add common helper functions for maintainability and flexibility. Please adjust the trait as you see fit. \ No newline at end of file diff --git a/movement-sdk/sui-helpers/sui-block-authority-providers/src/object_version/mod.rs b/movement-sdk/sui-helpers/sui-block-authority-providers/src/object_version/mod.rs new file mode 100644 index 000000000..6024b2266 --- /dev/null +++ b/movement-sdk/sui-helpers/sui-block-authority-providers/src/object_version/mod.rs @@ -0,0 +1 @@ +pub mod object_version_rocksdb; \ No newline at end of file diff --git a/movement-sdk/sui-helpers/sui-block-authority-providers/src/object_version/object_version_rocksdb.rs b/movement-sdk/sui-helpers/sui-block-authority-providers/src/object_version/object_version_rocksdb.rs new file mode 100644 index 000000000..fa9291fdf --- /dev/null +++ b/movement-sdk/sui-helpers/sui-block-authority-providers/src/object_version/object_version_rocksdb.rs @@ -0,0 +1,21 @@ +use sui_helper_types::{ + providers::object_version::ObjectVersionProvider, + block::VerifiedExecutableExecutionGroups +}; + +#[derive(Debug, Clone)] // would like for this to remain debug clone +pub struct ObjectVersionRocksDB { + +} + +#[async_trait::async_trait] +impl ObjectVersionProvider for ObjectVersionRocksDB { + + // todo: implement against rocksdb store + // todo: add methods to the trait in the `sui-helper-types` crate + + async fn assign_shared_object_versions(&self, transactions : VerifiedExecutableExecutionGroups) -> Result { + unimplemented!(); + } + +} \ No newline at end of file diff --git a/movement-sdk/sui-helpers/sui-block-authority-rpc/README.md b/movement-sdk/sui-helpers/sui-block-authority-rpc/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/movement-sdk/types/aptos-helper-types/Cargo.toml b/movement-sdk/types/aptos-helper-types/Cargo.toml new file mode 100644 index 000000000..cc098fa54 --- /dev/null +++ b/movement-sdk/types/aptos-helper-types/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "aptos-helper-types" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +# sui +aptos-types = { workspace = true } + +# general +futures = {workspace = true} +async-trait = { workspace = true } +anyhow = { workspace = true } +aptos-crypto = { workspace = true } \ No newline at end of file diff --git a/movement-sdk/types/aptos-helper-types/README.md b/movement-sdk/types/aptos-helper-types/README.md new file mode 100644 index 000000000..d4899e4b0 --- /dev/null +++ b/movement-sdk/types/aptos-helper-types/README.md @@ -0,0 +1,2 @@ +# `sui-helper-types` +Use this crate to compose types for reusability. diff --git a/movement-sdk/types/aptos-helper-types/src/block/block.rs b/movement-sdk/types/aptos-helper-types/src/block/block.rs new file mode 100644 index 000000000..3b0dfadde --- /dev/null +++ b/movement-sdk/types/aptos-helper-types/src/block/block.rs @@ -0,0 +1,11 @@ +use aptos_types::transaction::Transaction; +use aptos_crypto::hash::HashValue; + +#[derive(Debug, Clone)] +pub struct Block { + pub transactions: Vec, + pub block_id: HashValue, + pub parent_block_id: HashValue, + pub next_epoch : u64, + pub timestamp : u64, +} \ No newline at end of file diff --git a/movement-sdk/types/aptos-helper-types/src/block/mod.rs b/movement-sdk/types/aptos-helper-types/src/block/mod.rs new file mode 100644 index 000000000..b870750fb --- /dev/null +++ b/movement-sdk/types/aptos-helper-types/src/block/mod.rs @@ -0,0 +1,2 @@ +pub mod block; +pub use block::*; \ No newline at end of file diff --git a/movement-sdk/types/aptos-helper-types/src/lib.rs b/movement-sdk/types/aptos-helper-types/src/lib.rs new file mode 100644 index 000000000..49bc7d4bf --- /dev/null +++ b/movement-sdk/types/aptos-helper-types/src/lib.rs @@ -0,0 +1 @@ +pub mod block; \ No newline at end of file diff --git a/movement-sdk/types/canonical-types/Cargo.toml b/movement-sdk/types/canonical-types/Cargo.toml index a0bb537b1..24bf4255b 100644 --- a/movement-sdk/types/canonical-types/Cargo.toml +++ b/movement-sdk/types/canonical-types/Cargo.toml @@ -6,33 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -avalanche-types = { version = "0.0.398", features = ["subnet", "codec_base64"] } -tokio = { version = "1.25.0", features = ["fs", "rt-multi-thread"] } -tonic = { version = "0.8.3", features = ["gzip"] } -serde = { version = "1.0.152", features = ["derive"] } -serde_json = "1.0.93" # https://github.com/serde-rs/json/releases -serde_with = { version = "2.2.0", features = ["hex"] } -log = "0.4.17" -dirs = "5.0.1" -hex = "0.4.3" -bytes = "1.4.0" -env_logger = "0.10.0" -base64 = { version = "0.21.0" } -chrono = "0.4.23" -derivative = "2.2.0" -jsonrpc-core = "18.0.0" -jsonrpc-core-client = { version = "18.0.0" } -jsonrpc-derive = "18.0.0" -async-channel = "1.9.0" - -anyhow = { workspace = true } -futures = { workspace = true } -rand = { workspace = true } -bcs = { workspace = true } -movement-sdk = { workspace = true } - -# sui -sui-adapter-latest = { workspace = true } +aptos-types = { workspace = true } sui-types = { workspace = true } - -aptos-types = { workspace = true} +sui-helper-types = { workspace = true } +aptos-crypto = { workspace = true } +aptos-helper-types = { workspace = true } \ No newline at end of file diff --git a/movement-sdk/types/canonical-types/src/block/block.rs b/movement-sdk/types/canonical-types/src/block/block.rs index 943380a06..7d95d5ea2 100644 --- a/movement-sdk/types/canonical-types/src/block/block.rs +++ b/movement-sdk/types/canonical-types/src/block/block.rs @@ -1,25 +1,47 @@ // todo: reduce import depth use crate::transaction::transaction::Transaction; use aptos_types::transaction::Transaction as AptosTransaction; +use sui_types::transaction::SenderSignedData as SuiTransaction; +use aptos_crypto::hash::HashValue; +use aptos_helper_types::block::Block as AptosBlock; +use sui_helper_types::block::Block as SuiBlock; #[derive(Clone, Debug)] -pub struct Block(Vec); +pub struct Block { + pub transactions: Vec, + pub aptos_block_id: HashValue, + pub aptos_parent_block_id: HashValue, + pub aptos_next_epoch : u64, + pub aptos_timestamp : u64, +} impl Block { - pub fn new(transactions: Vec) -> Self { - Block(transactions) + pub fn new( + transactions: Vec, + aptos_block_id: HashValue, + aptos_parent_block_id: HashValue, + aptos_next_epoch : u64, + aptos_timestamp : u64, + ) -> Self { + Block { + transactions, + aptos_block_id, + aptos_parent_block_id, + aptos_next_epoch, + aptos_timestamp, + } } /// Filters transactions to those enums which contain Aptos transactions. pub fn filter_aptos_transactions(&self) -> Vec { - self.0.iter().filter(|t| t.is_aptos()).cloned().collect() + self.transactions.iter().filter(|t| t.is_aptos()).cloned().collect() } /// Extracts Aptos transactions refs from the block. - pub fn extract_aptos_transaction_refs(&self) -> Vec<&AptosTransaction> { - self.0.iter().filter_map(|transactions| { - match transactions { + pub fn get_aptos_transaction_refs(&self) -> Vec<&AptosTransaction> { + self.transactions.iter().filter_map(|transaction| { + match transaction { Transaction::Aptos(aptos_transaction) => Some(aptos_transaction), _ => None } @@ -27,25 +49,29 @@ impl Block { } /// Extracts Aptos transactions from the block. - pub fn extract_aptos_transactions(&self) -> Vec { - self.0.iter().filter_map(|transactions| { - match transactions { + pub fn get_aptos_transactions(&self) -> Vec { + self.transactions.iter().filter_map(|transaction| { + match transaction { Transaction::Aptos(aptos_transaction) => Some(aptos_transaction.clone()), _ => None } }).collect() } -} - -impl Into> for Block { - fn into(self) -> Vec { - self.0 + /// Gets an Aptos Block + pub fn get_aptos_block(&self) -> AptosBlock { + AptosBlock { + transactions: self.get_aptos_transactions(), + block_id: self.aptos_block_id, + parent_block_id: self.aptos_parent_block_id, + next_epoch: self.aptos_next_epoch, + timestamp: self.aptos_timestamp, + } } -} -impl Into for Vec { - fn into(self) -> Block { - Block(self) + /// Gets a Sui Block + pub fn get_sui_block(&self) -> SuiBlock { + unimplemented!(); } + } \ No newline at end of file diff --git a/movement-sdk/types/canonical-types/src/transaction/transaction.rs b/movement-sdk/types/canonical-types/src/transaction/transaction.rs index a99bbff1d..dbb24212b 100644 --- a/movement-sdk/types/canonical-types/src/transaction/transaction.rs +++ b/movement-sdk/types/canonical-types/src/transaction/transaction.rs @@ -1,5 +1,5 @@ use aptos_types::transaction::{Transaction as AptosTransaction}; -use sui_types::transaction::{Transaction as SuiTransaction}; +use sui_types::transaction::SenderSignedData as SuiTransaction; #[derive(Clone, Debug)] pub enum Transaction { diff --git a/movement-sdk/types/sui-helper-types/README.md b/movement-sdk/types/sui-helper-types/README.md new file mode 100644 index 000000000..d4899e4b0 --- /dev/null +++ b/movement-sdk/types/sui-helper-types/README.md @@ -0,0 +1,2 @@ +# `sui-helper-types` +Use this crate to compose types for reusability. diff --git a/movement-sdk/types/sui-helper-types/src/block/README.md b/movement-sdk/types/sui-helper-types/src/block/README.md new file mode 100644 index 000000000..c29716d70 --- /dev/null +++ b/movement-sdk/types/sui-helper-types/src/block/README.md @@ -0,0 +1,41 @@ +# `Block` +Sui Blocks are not natural to the Sui ecosystem, as is discussed elsewhere. However, Sui-like behavior can be achieved with blocks. The types herein represent a few different types of Sui blocks. + +## [Planned] `VerifiedExecutableBlock::get_max_parallel_groups` +`VerifiedExecutableBlock::get_max_parallel_groups` is a function that returns groups within a `VerifiedExecutableBlock` that can be executed in parallel. This is useful for parallelizing the execution of blocks. + +Within the context of Sui transactions, we can compute this value by looking at the sets of objects for each transaction block `SenderSignedData`. If the sets of objects are disjoint, then the blocks can be executed in parallel. If the sets of shared objects are not disjoint, then the blocks cannot be executed in parallel. + +You can obtain the sets of shared objects for each transaction block `SenderSignedData` by accessing the input objects on the [`TransactionData`](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-core/src/authority.rs#L963C78-L963C78); you should not need any additional data. + +You can implement the algorithm for computing this value using brute force, for an $O(n^2 * ||S||)$ complexity where $||S||$ is the size of the set of objects. + +This should look something like this: +```python +function find_parallel_groups(sets): + parallel_groups = [] // List to hold groups of parallel sets + for current_set in sets: + added_to_group = False + for group in parallel_groups: + if current_set is disjoint with every set in group: + add current_set to group + added_to_group = True + break + if not added_to_group: + create new group with current_set + add this new group to parallel_groups + return parallel_groups +``` + +You may also be able to implement a more efficient algorithm using a trie structure. But, I'm not sure... + +> I believe you can do better with a trie algorithm. But, I haven't had time to verify. It could look something like this: +> 1. Insert a null node as the root in the trie. +> 2. Sort all the elements in the shared object sets. +> 3. For each set, insert the elements in the set into the trie. +> 4. Insert the transaction hash as if it were the last element in the set. +> 5. Run each separate subtree from the root in parallel, by doing an in-order traversal to obtain the transaction hash then executing the corresponding transaction. +> The ordering of the elements in the sets ensures that intersecting sets will have the same path/subtree in the trie from the root. +> The complexity should be $O(n * ||S|| *log(||S||))$ I believe, which would be better in almost all cases because $||S||$ is likely to be smaller than $n$. However, I haven't had time to verify this. + +A more optimistic alternative is to use something similar to Block-STM with reruns on collisions. However, this can be entertained at a later date. \ No newline at end of file diff --git a/movement-sdk/types/sui-helper-types/src/block/block.rs b/movement-sdk/types/sui-helper-types/src/block/block.rs index 633cf213b..3a9c74a06 100644 --- a/movement-sdk/types/sui-helper-types/src/block/block.rs +++ b/movement-sdk/types/sui-helper-types/src/block/block.rs @@ -16,15 +16,34 @@ pub struct VerifiedExecutableBlock(Vec); #[derive(Debug, Clone)] pub struct VerifiedExecutableExecutionGroups(Vec>); +impl IntoIterator for VerifiedExecutableExecutionGroups { + type Item = Vec; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + impl VerifiedExecutableBlock { pub fn new(transactions : Vec) -> Self { Self(transactions) } - pub fn get_max_parrallel_groups(&self) -> VerifiedExecutableExecutionGroups { + pub fn get_max_parallel_groups(&self) -> VerifiedExecutableExecutionGroups { + // todo: see readme unimplemented!(); } +} + +impl IntoIterator for VerifiedExecutableBlock { + type Item = VerifiedExecutableTransaction; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } } \ No newline at end of file diff --git a/movement-sdk/types/sui-helper-types/src/providers/gas_info.rs b/movement-sdk/types/sui-helper-types/src/providers/gas_info.rs index 2d65b035a..c8b3a0a8b 100644 --- a/movement-sdk/types/sui-helper-types/src/providers/gas_info.rs +++ b/movement-sdk/types/sui-helper-types/src/providers/gas_info.rs @@ -8,7 +8,7 @@ use sui_types::{ }; #[async_trait::async_trait] -pub trait GasInforProvider { +pub trait GasInfoProvider { /// Provides up to date input objects for a transaction. /// Should be similar to this: https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/crates/sui-transaction-checks/src/lib.rs#L50 diff --git a/movement-sdk/types/sui-helper-types/src/providers/mod.rs b/movement-sdk/types/sui-helper-types/src/providers/mod.rs index d431a2f87..ca9239412 100644 --- a/movement-sdk/types/sui-helper-types/src/providers/mod.rs +++ b/movement-sdk/types/sui-helper-types/src/providers/mod.rs @@ -2,4 +2,4 @@ pub mod epoch; pub mod input_object; pub mod gas_info; pub mod verified_executable_transaction; -pub mod object_version_provider; \ No newline at end of file +pub mod object_version; \ No newline at end of file diff --git a/movement-sdk/types/sui-helper-types/src/providers/object_version.rs b/movement-sdk/types/sui-helper-types/src/providers/object_version.rs new file mode 100644 index 000000000..f6aa56354 --- /dev/null +++ b/movement-sdk/types/sui-helper-types/src/providers/object_version.rs @@ -0,0 +1,10 @@ +use crate::block::VerifiedExecutableExecutionGroups; + +// todo: expand this trait to include more analogs to these operations: https://github.com/MystenLabs/sui/blob/6ec723bcbdc4c36358d444cbfcd88ae1378761a5/crates/sui-core/src/authority/authority_per_epoch_store.rs#L301 +#[async_trait::async_trait] +pub trait ObjectVersionProvider { + + /// Assignes sequence numbers to objects in the transactions + async fn assign_shared_object_versions(&self, transactions : VerifiedExecutableExecutionGroups) -> Result; + +} \ No newline at end of file diff --git a/movement-sdk/types/sui-helper-types/src/providers/object_version_provider.rs b/movement-sdk/types/sui-helper-types/src/providers/object_version_provider.rs deleted file mode 100644 index fa474a794..000000000 --- a/movement-sdk/types/sui-helper-types/src/providers/object_version_provider.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::block::VerifiedExecutableExecutionGroups; - -#[async_trait::async_trait] -pub trait ObjectVersionProvider { - - async fn sequence_objects_for_transactions(&self, transactions : VerifiedExecutableExecutionGroups) -> Result; - -} \ No newline at end of file From d5b7d4041937d2835ac3cdd047e1cf45695f29b6 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 7 Dec 2023 09:34:30 -1000 Subject: [PATCH 58/58] docs: recommended readings and viewsings. --- movement-sdk/CANONICAL.md | 65 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 movement-sdk/CANONICAL.md diff --git a/movement-sdk/CANONICAL.md b/movement-sdk/CANONICAL.md new file mode 100644 index 000000000..c0c826f60 --- /dev/null +++ b/movement-sdk/CANONICAL.md @@ -0,0 +1,65 @@ +# Canonical VM +This doc serves as a general developer onboarding and point of reference for Canonical VM development. It is intended to be a living document that is updated as the project evolves. More detailed documentation can be found in the subfolders associated with various components of the project. + +## Overview +The Canonical VM is a virtual machine and storage layer solution to the Move VM interoperability. It commits to the idea that modifying the various storage resolvers and natives associated with the different Move VMs can allow them to operate over the same derived state without having to completely reproduce the VMs individually. + +Currently, the greatest challenge facing the Canonical VM project is the difficulty presented by Sui's object runtime which is closely entangled with its unique for of consensus. This is discussed at length elsewhere, and while working on this project you will become closely acquainted with these challenges. The bottom line is: Sui doesn't have traditional blocks and there's a lot you have to do to make Sui-like execution work outside of the Sui network. + +## Getting Started +Here are resources that will help you to get started with the project. Some of them are traditional reading material, other videos, others are particularly important docs and code snippets. + +### Recommended Reading and Viewing +Before diving into the various crates in this workspace, we recommend you spend considerable time considering the following resources: + +#### Sui Transactions +- READ: https://docs.sui.io/concepts/transactions +- READ: https://docs.sui.io/guides/developer/sui-101/shared-owned +- READ and TRY: https://docs.sui.io/guides/developer/sui-101/building-ptb +- READ: https://medium.com/@0xZorz/fundamentals-of-parallelising-smart-contract-execution-8e75694697c3 +- READ: https://academy.glassnode.com/concepts/utxo + +Sui has a very unique approach to transactions. It is important to understand how they work and how they interface with objects. + +**💡 Key Concepts** +- Sui objects--particularly shared objects--[are used to determine ownership and which transactions can run in parallel](https://docs.sui.io/concepts/transactions#transactions-flow---example). +- [Programmable Transaction Blocks](https://docs.sui.io/concepts/transactions/prog-txn-blocks) are a collection of transactions usually submitted by a single user. They are grouped together for performing large operations quickly and smashing gas. + - PTBs also help counteract a problem in some UTXO models that prevents mutating owned state multiple times in the same block. +- When a transaction [owns all of its objects](https://docs.sui.io/concepts/transactions/transaction-lifecycle), it is actually not involved in being ordered in a consensus protocol. It is simply executed and all of the cryptographic checks rely on the underlying uniqueness of the object ID, various validity checks, and the signer and validator signatures. + - In the Canonical VM, we are not initially attempting to implement this fast path. We are sticking with setting up our various subsystems to handle block-based consensus. +- Sui combines a UTXO and account-based approach to blockchain accounting. Usually, you'll hear people say that it's the owned transactions which are essentially UTXO and the shared transactions which are essentially account-based. For our purposes, we will only be really interested in this distinction when comes to attempting to provide user conveniences on for dealing with Aptos-accounts and Sui Objects. Initially, however, the development plan is to keep these methods of accounting largely separate. + +#### Narwhal Mempool and Bullshark Consensus +- WATCH: https://www.youtube.com/watch?v=xKDDuPrYUag +- READ: https://docs.sui.io/concepts/sui-architecture/consensus + +While we are not implementing Narwhal Consensus for any of the layers involved in the current Canonical VM push, having some sense of it often helps to understand the decisions made in the Sui source. + +**💡 Key Concepts** +- Narwhal is a high-throughput DAG-based mempool and Bullshark Consensus capitalizes on this with DAG-based fork-selection scheme. +- Transactions are tracked through this mempool based on their input objects. + +#### The Sui Source +- INSPECT: https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/sui-execution/src/latest.rs#L79 +- INSPECT: https://github.com/MystenLabs/sui/blob/dd4514d5d7ebffadcd25ac6e0cbf9a63e375dcf7/crates/sui-core/src/authority.rs#L1339 +- INSPECT: https://github.com/MystenLabs/sui/blob/dd4514d5d7ebffadcd25ac6e0cbf9a63e375dcf7/crates/sui-core/src/authority.rs#L950 + +[`execute_transaction_to_effects`](https://github.com/MystenLabs/sui/blob/552158d9eae200314499809d8977f732f6c2cee7/sui-execution/src/latest.rs#L79) is the highest level of execution where don't actually have to get entangled with Sui's consensus and transactions lifecycle. However, we want to draw heavily from the approaches put forth in the path between [`try_execute_immediately`](https://github.com/MystenLabs/sui/blob/dd4514d5d7ebffadcd25ac6e0cbf9a63e375dcf7/crates/sui-core/src/authority.rs#L1339) and [`prepare_certificate`](https://github.com/MystenLabs/sui/blob/dd4514d5d7ebffadcd25ac6e0cbf9a63e375dcf7/crates/sui-core/src/authority.rs#L950). + +**💡 Key Concepts** +- There is a path that ostensibly allows us to do and/or mimic the object runtime without having to get entangled with consensus and the transaction lifecycle too deeply. It requires, however, implementing a lot dependencies that currently are provided from that domain. +- If the above turns out not to be the case, we essentially have to start looking going a bit lower to replace `execute_transaction_to_effects` and building many of the same dependencies that are currently scoped. +- Maybe we're wrong? Do you see something new? + +### Our Source +READ: https://github.com/movemntdev/M1/tree/dev/movement-sdk/execution/sui-block-executor +INTERNAL READ: https://github.com/movemntdev/org/blob/main/projects/Canonical.md + +Both of the above document progressive learning through the project. Please absorb and critique the findings. + +**💡 Key Concepts** +- We began by trying to do what we are now attempting, i.e., to use `execute_transaction_to_effects`. +- We were dissuaded from the above approach by Mysten Labs when we thought that the `CheckpointExecutor` might be a better analog. +- We were dissuaded from the `CheckpointExecutor` approach when we realized that it would be more difficult to make the necessary modifications. +- We are now back to attempting to use `execute_transaction_to_effects` and are trying to build the dependencies that we need to do so. +- **!!![The Dependencies of `execute_transaction_to_effects`](https://github.com/movemntdev/M1/tree/dev/movement-sdk/execution/sui-block-executor#the-dependencies-of-execute_transaction_to_effects) is perhaps the most useful section once you have a sense o things. It maps out the dependencies we need to implement. !!!** \ No newline at end of file