diff --git a/CLI.md b/CLI.md new file mode 100644 index 000000000..9fe47e529 --- /dev/null +++ b/CLI.md @@ -0,0 +1,190 @@ +# Command Line Interface + +The `dfx-orbit` command line is a tool for interacting with Orbit. Currently it focuses on deploying canisters controlled by Orbit but the scope is expected to grow. + +## Getting started + +### Installation +Build the tool: +``` +$ cargo build -p dfx-orbit +``` + +Verify that the tool works: +``` +$ ./target/debug/dfx-orbit --version +dfx-orbit 0.1.0 + +$ ./target/debug/dfx-orbit --help +Command line tool for interacting with the Orbit digital asset manager on the ICP blockchain. + +Usage: dfx-orbit + +Commands: + station Manages Orbit stations +... +``` + +Add `dfx-orbit` to your `PATH`. + +### Connect to Orbit + +Connect your local dfx identity to your Orbit identity: + +* Log in to Orbit. +* Navigate to Settings -> Users -> Edit a user -> Identities +* Add the principal provided by `dfx identity get-principal` + +Tell the command line tool where to find the orbit station: + +* Log in to Orbit. +* Navigate to station settings. +* Copy the wallet ID +* Store the station details locally. If your wallet is called `shiny` and is running locally, the command is: + ``` + dfx-orbit station add shiny --station-id "$WALLET_ID" --network local --url https://orbitwallet.io + ``` +* Verify that the station is in your list of stations: + ``` + dfx-orbit station list + ``` +* If you have multiple stations, set this as your default: + ``` + dfx-orbit station use shiny + ``` +* Show the station details + ``` + dfx-orbit station show + ``` +* In the orbit web UI, create a user with your local dfx principal: + ``` + dfx identity get-principal + ``` +* Verify that you can get your profile on the Orbit station: + ``` + dfx-orbit me + ``` + +TODO: The Oisy canister ID is also called the wallet ID and the station ID. Consistent nomenclature that doesn't conflict with established terminology would be nice. + +### Grant permission to make requests +You can check which permissions you have with: +``` +dfx-orbit me | jq .Ok.privileges +``` +Initially you are likely to have only permission to see your own profile: +``` +[ + "Capabilities" +] +``` + +Without permission to make and view requests, you will not be able to do much. It is recommended to make a `Developer` group with the following permissions: + +| Name in UI | Privilege in `dfx-orbit me` | Name in error messages | Used for | +|--------------|-----------------------------|------------------------|----------------------------------| +| Request/List | `ListRequests` | `Request(List)` | `dfx-orbit review list` | +| Request/Read | Not Shown | `Request(Read(Id` | `dfx-orbit review id REQUEST_ID` | + +TODO: It would be nice to be able to link directly to a permission. E.g. this could open the permissions page and focus on one specific permission: https://orbitwallet.io/en/settings/user-groups/permissions#Request/List + + +## Make canister calls with Orbit +Instead of using `dfx canister call CANISTER METHOD ARGUMENTS` use `dfx-orbit request canister call CANISTER METHOD ARGUMENTS`. + +For example, asset canisters have the methods `list_authorized` and `list_permitted`. You should be able to make these canister calls directly or via Orbit: +``` +dfx canister call frontend list_authorized +dfx-orbit request canister call frontend list_authorized +``` + + +## Control a canister with Orbit + +### Grant Orbit control of the canister +Assume that you have a canister called `MY_CANISTER` in `dfx`. You may also refer to your canister by canister ID. + +Check the current controllers of the canister: +``` +dfx canister info MY_CANISTER --network MY_CANISTER_NETWORK +``` + +Add Orbit as a controller of the canister: +``` +dfx-orbit canister claim MY_CANISTER +``` +Verify that Orbit has been added as a controller: +``` +dfx canister info MY_CANISTER --network MY_CANISTER_NETWORK +``` + +### Upgrade canisters +#### Request permission to make upgrade requests +This will allow you to propose upgrades to `MY_CANISTER`: + +``` +dfx-orbit request permission canister change wasm --canister MY_CANISTER +``` +This will create an Orbit request. Once approved you will be able to propose canister upgrades. + +> :warning: **The Orbit GUI does not currently show this proposal unless you enter the proposal URL directly, under /en/settings/requests?reqid=THE_ID** + +#### Request a canister upgrade +Suppose that you have built a new Wasm and put a copy at `./MY-CANISTER.wasm.gz`. To upgrade your canister to the new Wasm: +``` +dfx-orbit request canister change wasm --canister MY_CANISTER --mode upgrade --wasm ./MY-CANISTER.wasm.gz +``` + +### Upload assets to a canister +We will assume that Orbit is a controller of the asset canister. If not, please adapt the following commands by using `dfx canister call` instead of `dfx-orbit request canister call`. + +#### Authorize the developer to upload assets +Note: Uploaded assets are not published. They are only prepared for release. +``` +developer_principal="$(dfx identity get-principal)" +dfx-orbit request canister call frontend grant_permission " +( + record { + permission = variant { Prepare }; + to_principal = principal \"$developer_principal\"; + }, +) +" +``` +When the request has been approved, check the list of principals permitted to prepare assets: +``` +dfx canister call frontend list_permitted '(record { permission = variant { Prepare } })' +``` + +#### Authorize the orbit station to commit assets +Note: Committing uploaded assets causes them to be published on the asset canister web site. +``` +station_principal="$(dfx-orbit station show | jq -r .station_id)" +dfx-orbit request canister call frontend grant_permission " +( + record { + permission = variant { Commit }; + to_principal = principal \"$station_principal\"; + }, +) +" +``` +When the request has been approved, check the list of principals permitted to commit assets: +``` +dfx canister call frontend list_permitted '(record { permission = variant { Commit } })' +``` + +#### Request an asset update +A developer may upload one or more directories of HTTP assets with: +``` +dfx-orbit canister upload-http-assets --canister CANISTER_NAME --source SOME_DIR/ --source OTHER_DIR/ +``` +The developer may now request that the assets be published. The command for this is printed at the end of the upload command. Example: +``` +... +Jul 03 09:36:42.148 INFO Computing evidence. +Proposed batch_id: 5 +Assets have been uploaded. For the changes to take effect, run: +dfx-orbit request canister call frontend commit_proposed_batch '(record { batch_id = 5 : nat; evidence = blob "\e3\b0\c4\42\98\fc\1c\14\9a\fb\f4\c8\99\6f\b9\24\27\ae\41\e4\64\9b\93\4c\a4\95\99\1b\78\52\b8\55" })' +``` +Once the request has been approved, the changes will take effect. \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 0f804104d..07b229744 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,65 @@ 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.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher 0.3.0", + "cpufeatures", + "opaque-debug", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher 0.4.4", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes 0.8.4", + "cipher 0.4.4", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -26,12 +85,67 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "ambient-authority" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" + +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + [[package]] name = "anstyle" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" version = "1.0.82" @@ -44,6 +158,17 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +[[package]] +name = "argon2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73" +dependencies = [ + "base64ct", + "blake2", + "password-hash", +] + [[package]] name = "arrayvec" version = "0.5.2" @@ -59,6 +184,46 @@ dependencies = [ "term", ] +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite", + "log", + "parking", + "polling", + "rustix 0.37.27", + "slab", + "socket2 0.4.10", + "waker-fn", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-trait" version = "0.1.80" @@ -76,6 +241,20 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "backoff" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" +dependencies = [ + "futures-core", + "getrandom", + "instant", + "pin-project-lite", + "rand", + "tokio", +] + [[package]] name = "backtrace" version = "0.3.71" @@ -91,18 +270,42 @@ dependencies = [ "rustc-demangle", ] +[[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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "beef" version = "0.5.2" @@ -132,6 +335,24 @@ dependencies = [ "syn 1.0.109", ] +[[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 0.11.6", + "once_cell", + "pbkdf2", + "rand_core", + "ripemd", + "sha2 0.10.8", + "subtle", + "zeroize", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -159,6 +380,24 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[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 = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -168,12 +407,71 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-modes" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" +dependencies = [ + "block-padding", + "cipher 0.3.0", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "bls12_381" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3c196a77437e7cc2fb515ce413a6401291578b5afc8ecb29a3c7ab957f05941" +dependencies = [ + "digest 0.9.0", + "ff 0.12.1", + "group 0.12.1", + "pairing", + "rand_core", + "subtle", +] + +[[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 = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "byte-unit" +version = "4.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da78b32057b8fdfc352504708feeba7216dcd65a2c9ab02978cbd288d1279b6c" +dependencies = [ + "serde", + "utf8-width", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -186,6 +484,52 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +[[package]] +name = "cached" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c8c50262271cdf5abc979a5f76515c234e764fa025d1ba4862c0f0bcda0e95" +dependencies = [ + "ahash", + "hashbrown", + "instant", + "once_cell", + "thiserror", +] + +[[package]] +name = "cached" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b0116662497bc24e4b177c90eaf8870e39e2714c3fcfa296327a93f593fc21" +dependencies = [ + "ahash", + "cached_proc_macro", + "cached_proc_macro_types", + "hashbrown", + "instant", + "once_cell", + "thiserror", +] + +[[package]] +name = "cached_proc_macro" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c878c71c2821aa2058722038a59a67583a4240524687c6028571c9b395ded61f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cached_proc_macro_types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" + [[package]] name = "canbench-rs" version = "0.1.1" @@ -222,7 +566,7 @@ dependencies = [ "hex", "ic_principal", "leb128", - "num-bigint", + "num-bigint 0.4.5", "num-traits", "paste", "pretty", @@ -258,7 +602,7 @@ dependencies = [ "lalrpop", "lalrpop-util", "logos", - "num-bigint", + "num-bigint 0.4.5", "pretty", "thiserror", ] @@ -272,12 +616,41 @@ dependencies = [ "futures", "ic-cdk 0.13.2", "ic-cdk-timers", - "num-bigint", + "num-bigint 0.4.5", "serde", "serde_bytes", "thiserror", ] +[[package]] +name = "cap-primitives" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00172660727e2d7f808e7cc2bfffd093fdb3ea2ff2ef819289418a3c3ffab5ac" +dependencies = [ + "ambient-authority", + "fs-set-times", + "io-extras", + "io-lifetimes 2.0.3", + "ipnet", + "maybe-owned", + "rustix 0.38.34", + "windows-sys 0.52.0", + "winx", +] + +[[package]] +name = "cap-std" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd9187bb3f7478a4c135ea10473a41a5f029d2ac800c1adf64f35ec7d4c8603" +dependencies = [ + "cap-primitives", + "io-extras", + "io-lifetimes 2.0.3", + "rustix 0.38.34", +] + [[package]] name = "cc" version = "1.0.96" @@ -291,40 +664,142 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "codespan-reporting" -version = "0.11.1" +name = "cipher" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "termcolor", - "unicode-width", + "generic-array", ] [[package]] -name = "control-panel" -version = "0.0.2-alpha.2" +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "candid", - "candid_parser", - "canfund", - "control-panel-api", - "email_address", - "hex", - "ic-cdk 0.13.2", - "ic-cdk-macros 0.9.0", - "ic-cdk-timers", - "ic-stable-structures", - "lazy_static", - "orbit-essentials", - "rstest", - "semver", - "serde", - "serde_cbor", - "serde_json", - "station-api", - "thiserror", - "tokio", - "uuid", + "crypto-common", + "inout", +] + +[[package]] +name = "clap" +version = "4.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.11.1", +] + +[[package]] +name = "clap_derive" +version = "4.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "clap_lex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "control-panel" +version = "0.0.2-alpha.2" +dependencies = [ + "candid", + "candid_parser", + "canfund", + "control-panel-api", + "email_address", + "hex", + "ic-cdk 0.13.2", + "ic-cdk-macros 0.9.0", + "ic-cdk-timers", + "ic-stable-structures", + "lazy_static", + "orbit-essentials", + "rstest", + "semver", + "serde", + "serde_cbor", + "serde_json", + "station-api", + "thiserror", + "tokio", + "uuid", ] [[package]] @@ -401,6 +876,30 @@ 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", + "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", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -408,15 +907,105 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core", "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher 0.4.4", +] + +[[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", + "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", + "darling_macro", +] + +[[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", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + [[package]] name = "data-encoding" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468 0.7.0", + "zeroize", +] + [[package]] name = "deranged" version = "0.3.11" @@ -426,15 +1015,150 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "deunicode" version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322ef0094744e63628e6f0eb2295517f79276a5b342a4c2ff3042566ca181d4e" +[[package]] +name = "dfx-core" +version = "0.0.1" +source = "git+https://github.com/dfinity/sdk.git?tag=0.20.2-beta.0#06eaa58f9c52d27a015cd8f754ecd3d0d0ba555b" +dependencies = [ + "aes-gcm", + "argon2", + "bip32", + "byte-unit", + "bytes", + "candid", + "clap", + "dialoguer", + "directories-next", + "dunce", + "flate2", + "handlebars", + "hex", + "humantime-serde", + "ic-agent", + "ic-identity-hsm", + "ic-utils", + "k256 0.11.6", + "keyring", + "lazy_static", + "reqwest", + "ring 0.16.20", + "schemars", + "sec1 0.3.0", + "semver", + "serde", + "serde_json", + "slog", + "tar", + "tempfile", + "thiserror", + "time", + "tiny-bip39", + "url", +] + +[[package]] +name = "dfx-core" +version = "0.0.1" +source = "git+https://github.com/dfinity/sdk.git?rev=ae10a96b381cfce3d8ac5a6cb940d19224ea6d2e#ae10a96b381cfce3d8ac5a6cb940d19224ea6d2e" +dependencies = [ + "aes-gcm", + "argon2", + "bip32", + "byte-unit", + "bytes", + "candid", + "clap", + "dialoguer", + "directories-next", + "dunce", + "flate2", + "handlebars", + "hex", + "humantime-serde", + "ic-agent", + "ic-identity-hsm", + "ic-utils", + "k256 0.11.6", + "keyring", + "lazy_static", + "reqwest", + "ring 0.16.20", + "schemars", + "sec1 0.3.0", + "semver", + "serde", + "serde_json", + "slog", + "tar", + "tempfile", + "thiserror", + "time", + "tiny-bip39", + "url", +] + [[package]] name = "dfx-orbit" version = "0.1.0" +dependencies = [ + "anyhow", + "candid", + "candid_parser", + "cap-std", + "clap", + "dfx-core 0.0.1 (git+https://github.com/dfinity/sdk.git?tag=0.20.2-beta.0)", + "ic-agent", + "ic-asset", + "ic-certified-assets", + "ic-utils", + "serde", + "serde_json", + "slog", + "slog-async", + "slog-term", + "station-api", + "tokio", + "walkdir", +] + +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console", + "shell-words", + "tempfile", + "thiserror", + "zeroize", +] + +[[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" @@ -442,8 +1166,20 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", + "const-oid", "crypto-common", + "subtle", +] + +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if", + "dirs-sys-next", ] [[package]] @@ -473,18 +1209,106 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + [[package]] name = "dyn-clone" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +[[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.9", + "digest 0.10.7", + "elliptic-curve 0.13.8", + "rfc6979 0.4.0", + "signature 2.2.0", + "spki 0.7.3", +] + +[[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", + "serde", + "sha2 0.9.9", + "thiserror", + "zeroize", +] + [[package]] name = "either" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +[[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", + "pem-rfc7468 0.6.0", + "pkcs8 0.9.0", + "rand_core", + "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", + "sec1 0.7.3", + "subtle", + "zeroize", +] + [[package]] name = "email_address" version = "0.2.4" @@ -503,18 +1327,140 @@ dependencies = [ "log", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "enumflags2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83c8d82922337cd23a15f88b70d8e4ef5f11da38dd7cdb55e84dd5de99695da0" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.1", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + [[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.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "libz-ng-sys", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -536,6 +1482,17 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" +[[package]] +name = "fs-set-times" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033b337d725b97690d86893f9de22b67b80dcc4e9ad815f348254c38119db8fb" +dependencies = [ + "io-lifetimes 2.0.3", + "rustix 0.38.34", + "windows-sys 0.52.0", +] + [[package]] name = "futures" version = "0.3.30" @@ -578,12 +1535,38 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot 0.11.2", +] + [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + [[package]] name = "futures-macro" version = "0.3.30" @@ -639,6 +1622,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -653,16 +1637,61 @@ dependencies = [ ] [[package]] -name = "gimli" -version = "0.28.1" +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[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", + "log", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[package]] +name = "group" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core", + "subtle", +] [[package]] -name = "glob" -version = "0.3.1" +name = "group" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", + "rand_core", + "subtle", +] [[package]] name = "h2" @@ -675,7 +1704,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 1.1.0", "indexmap", "slab", "tokio", @@ -689,11 +1718,41 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" +[[package]] +name = "handlebars" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -710,6 +1769,46 @@ dependencies = [ "serde", ] +[[package]] +name = "hkdf" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b" +dependencies = [ + "digest 0.9.0", + "hmac 0.11.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac", + "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 = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.1.0" @@ -728,7 +1827,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http", + "http 1.1.0", ] [[package]] @@ -739,7 +1838,7 @@ checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" dependencies = [ "bytes", "futures-core", - "http", + "http 1.1.0", "http-body", "pin-project-lite", ] @@ -750,6 +1849,22 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + [[package]] name = "hyper" version = "1.3.1" @@ -760,7 +1875,7 @@ dependencies = [ "futures-channel", "futures-util", "h2", - "http", + "http 1.1.0", "http-body", "httparse", "itoa", @@ -777,7 +1892,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", - "http", + "http 1.1.0", "hyper", "hyper-util", "rustls", @@ -796,17 +1911,100 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", + "http 1.1.0", "http-body", "hyper", "pin-project-lite", - "socket2", + "socket2 0.5.7", "tokio", "tower", "tower-service", "tracing", ] +[[package]] +name = "ic-agent" +version = "0.35.0" +source = "git+https://github.com/dfinity/agent-rs.git?rev=8273d321e9a09fd8373bd4e38b0676ec6ad9c260#8273d321e9a09fd8373bd4e38b0676ec6ad9c260" +dependencies = [ + "async-lock 3.4.0", + "backoff", + "cached 0.46.1", + "candid", + "ed25519-consensus", + "futures-util", + "hex", + "http 1.1.0", + "http-body", + "ic-certification", + "ic-transport-types", + "ic-verify-bls-signature", + "k256 0.13.3", + "leb128", + "p256", + "pem", + "pkcs8 0.10.2", + "rand", + "rangemap", + "reqwest", + "ring 0.17.8", + "rustls-webpki 0.101.7", + "sec1 0.7.3", + "serde", + "serde_bytes", + "serde_cbor", + "serde_repr", + "sha2 0.10.8", + "simple_asn1", + "thiserror", + "time", + "tokio", + "url", +] + +[[package]] +name = "ic-asset" +version = "0.20.0" +source = "git+https://github.com/dfinity/sdk.git?rev=ae10a96b381cfce3d8ac5a6cb940d19224ea6d2e#ae10a96b381cfce3d8ac5a6cb940d19224ea6d2e" +dependencies = [ + "backoff", + "candid", + "derivative", + "dfx-core 0.0.1 (git+https://github.com/dfinity/sdk.git?rev=ae10a96b381cfce3d8ac5a6cb940d19224ea6d2e)", + "flate2", + "futures", + "futures-intrusive", + "globset", + "hex", + "ic-agent", + "ic-utils", + "itertools 0.10.5", + "json5", + "mime", + "mime_guess", + "serde", + "serde_bytes", + "serde_json", + "sha2 0.10.8", + "slog", + "thiserror", + "tokio", + "walkdir", +] + +[[package]] +name = "ic-cbor" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc917070b8fc4bd88e3199746372e44d507f54c93a9b191787e1caefca1eba" +dependencies = [ + "candid", + "ic-certification", + "leb128", + "nom", + "thiserror", +] + [[package]] name = "ic-cdk" version = "0.12.1" @@ -889,6 +2087,85 @@ dependencies = [ "slotmap", ] +[[package]] +name = "ic-certificate-verification" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "769142849e241e6cf7f5611f9b04983e958729495ea67d2de95e5d9a9c687d9b" +dependencies = [ + "cached 0.47.0", + "candid", + "ic-cbor", + "ic-certification", + "lazy_static", + "leb128", + "miracl_core_bls12381", + "nom", + "parking_lot 0.12.2", + "sha2 0.10.8", + "thiserror", +] + +[[package]] +name = "ic-certification" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20052ce9255fbe2de7041a4f6996fddd095ba1f31ae83b6c0ccdee5be6e7bbcf" +dependencies = [ + "hex", + "serde", + "serde_bytes", + "sha2 0.10.8", +] + +[[package]] +name = "ic-certified-assets" +version = "0.2.5" +source = "git+https://github.com/dfinity/sdk.git?rev=ae10a96b381cfce3d8ac5a6cb940d19224ea6d2e#ae10a96b381cfce3d8ac5a6cb940d19224ea6d2e" +dependencies = [ + "base64 0.13.1", + "candid", + "hex", + "ic-cdk 0.13.2", + "ic-certification", + "ic-representation-independent-hash", + "ic-response-verification", + "itertools 0.10.5", + "num-traits", + "serde", + "serde_bytes", + "serde_cbor", + "sha2 0.10.8", +] + +[[package]] +name = "ic-http-certification" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ddb96501529c2380e087fa9f4552fd0d416f5784bb1e48142d746e9b3d6ae13" +dependencies = [ + "candid", + "http 0.2.12", + "ic-certification", + "ic-representation-independent-hash", + "serde", + "thiserror", + "urlencoding", +] + +[[package]] +name = "ic-identity-hsm" +version = "0.35.0" +source = "git+https://github.com/dfinity/agent-rs.git?rev=8273d321e9a09fd8373bd4e38b0676ec6ad9c260#8273d321e9a09fd8373bd4e38b0676ec6ad9c260" +dependencies = [ + "hex", + "ic-agent", + "pkcs11", + "sha2 0.10.8", + "simple_asn1", + "thiserror", +] + [[package]] name = "ic-ledger-types" version = "0.10.0" @@ -901,7 +2178,41 @@ dependencies = [ "ic-cdk 0.13.2", "serde", "serde_bytes", - "sha2", + "sha2 0.10.8", +] + +[[package]] +name = "ic-representation-independent-hash" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4d9c969c80e9b445255341da79772680f503ef856b95b3ddf162b41d096df1f" +dependencies = [ + "leb128", + "sha2 0.10.8", +] + +[[package]] +name = "ic-response-verification" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69ce81210d3747a4e334a86851c57725a5ec4d2d0c40b05b5dc4f9114c19c78a" +dependencies = [ + "base64 0.21.7", + "candid", + "flate2", + "hex", + "http 0.2.12", + "ic-cbor", + "ic-certificate-verification", + "ic-certification", + "ic-http-certification", + "ic-representation-independent-hash", + "leb128", + "log", + "nom", + "sha2 0.10.8", + "thiserror", + "urlencoding", ] [[package]] @@ -913,6 +2224,55 @@ dependencies = [ "ic_principal", ] +[[package]] +name = "ic-transport-types" +version = "0.35.0" +source = "git+https://github.com/dfinity/agent-rs.git?rev=8273d321e9a09fd8373bd4e38b0676ec6ad9c260#8273d321e9a09fd8373bd4e38b0676ec6ad9c260" +dependencies = [ + "candid", + "hex", + "ic-certification", + "leb128", + "serde", + "serde_bytes", + "serde_repr", + "sha2 0.10.8", + "thiserror", +] + +[[package]] +name = "ic-utils" +version = "0.35.0" +source = "git+https://github.com/dfinity/agent-rs.git?rev=8273d321e9a09fd8373bd4e38b0676ec6ad9c260#8273d321e9a09fd8373bd4e38b0676ec6ad9c260" +dependencies = [ + "async-trait", + "candid", + "futures-util", + "ic-agent", + "once_cell", + "semver", + "serde", + "serde_bytes", + "sha2 0.10.8", + "strum", + "strum_macros", + "thiserror", + "time", + "tokio", +] + +[[package]] +name = "ic-verify-bls-signature" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583b1c03380cf86059160cc6c91dcbf56c7b5f141bf3a4f06bc79762d775fac4" +dependencies = [ + "bls12_381", + "lazy_static", + "pairing", + "sha2 0.9.9", +] + [[package]] name = "ic0" version = "0.21.1" @@ -929,10 +2289,16 @@ dependencies = [ "crc32fast", "data-encoding", "serde", - "sha2", + "sha2 0.10.8", "thiserror", ] +[[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.5.0" @@ -953,6 +2319,24 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + [[package]] name = "integration-tests" version = "0.0.1" @@ -964,21 +2348,74 @@ dependencies = [ "ic-cdk 0.13.2", "ic-ledger-types", "lazy_static", - "num-bigint", + "num-bigint 0.4.5", "orbit-essentials", "pocket-ic", "serde", - "sha2", + "sha2 0.10.8", "station-api", "upgrader-api", "wat", ] [[package]] -name = "ipnet" -version = "2.9.0" +name = "io-extras" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9f046b9af244f13b3bd939f55d16830ac3a201e8a9ba9661bfcb03e2be72b9b" +dependencies = [ + "io-lifetimes 2.0.3", + "windows-sys 0.52.0", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "io-lifetimes" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c" + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] [[package]] name = "itertools" @@ -1004,6 +2441,65 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[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", +] + +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", + "once_cell", + "sha2 0.10.8", + "signature 2.2.0", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keyring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba264b266563c1363dcce004776cbf198d7422a4262f77f4ca285bf26ae30955" +dependencies = [ + "byteorder", + "secret-service", + "security-framework", + "winapi", +] + [[package]] name = "lalrpop" version = "0.20.2" @@ -1013,7 +2509,7 @@ dependencies = [ "ascii-canvas", "bit-set", "ena", - "itertools", + "itertools 0.11.0", "lalrpop-util", "petgraph", "pico-args", @@ -1053,6 +2549,16 @@ version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +[[package]] +name = "libloading" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +dependencies = [ + "cc", + "winapi", +] + [[package]] name = "libredox" version = "0.1.3" @@ -1063,6 +2569,28 @@ dependencies = [ "libc", ] +[[package]] +name = "libz-ng-sys" +version = "1.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6409efc61b12687963e602df8ecf70e8ddacf95bc6576bcf16e3ac6328083c5" +dependencies = [ + "cmake", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -1120,18 +2648,49 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "maybe-owned" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" + [[package]] name = "memchr" version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[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 = "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.2" @@ -1152,6 +2711,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "miracl_core_bls12381" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07cbe42e2a8dd41df582fb8e00fc24d920b5561cc301fcb6d14e2e0434b500f" + [[package]] name = "mockall" version = "0.12.1" @@ -1179,12 +2744,45 @@ dependencies = [ "syn 2.0.60", ] +[[package]] +name = "nb-connect" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1bb540dc6ef51cfe1916ec038ce7a620daf3a111e2502d745197cd53d6bca15" +dependencies = [ + "libc", + "socket2 0.4.10", +] + [[package]] name = "new_debug_unreachable" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if", + "libc", + "memoffset", +] + +[[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 = "nu-ansi-term" version = "0.46.0" @@ -1195,18 +2793,51 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint 0.4.5", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" dependencies = [ "autocfg", "num-integer", "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +dependencies = [ + "num-integer", + "num-traits", "serde", ] +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -1222,11 +2853,33 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint 0.4.5", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -1256,6 +2909,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "openssl-probe" version = "0.1.5" @@ -1280,7 +2939,7 @@ dependencies = [ "serde_bytes", "serde_cbor", "serde_json", - "sha2", + "sha2 0.10.8", "thiserror", "time", "tokio", @@ -1313,40 +2972,185 @@ 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.9", + "elliptic-curve 0.13.8", + "primeorder", + "sha2 0.10.8", +] + +[[package]] +name = "pairing" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135590d8bdba2b31346f9cd1fb2a912329f5135e832a4f422942eb6ead8b6b3b" +dependencies = [ + "group 0.12.1", +] + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[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.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.10", +] + +[[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.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.1", + "smallvec", + "windows-targets 0.52.5", +] + +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[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 = "pem" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b13fe415cdf3c8e44518e18a7c95a13431d9bdf6d15367d82b23c377fdd441a" +dependencies = [ + "base64 0.21.7", + "serde", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" dependencies = [ - "lock_api", - "parking_lot_core", + "memchr", + "thiserror", + "ucd-trie", ] [[package]] -name = "parking_lot_core" -version = "0.9.10" +name = "pest_derive" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.5", + "pest", + "pest_generator", ] [[package]] -name = "paste" -version = "1.0.14" +name = "pest_generator" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.60", +] [[package]] -name = "percent-encoding" -version = "2.3.1" +name = "pest_meta" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" +dependencies = [ + "once_cell", + "pest", + "sha2 0.10.8", +] [[package]] name = "petgraph" @@ -1405,6 +3209,36 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs11" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aca6d67e4c8613bfe455599d0233d00735f85df2001f6bfd9bb7ac0496b10af" +dependencies = [ + "libloading", + "num-bigint 0.2.6", +] + +[[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.9", + "spki 0.7.3", +] + [[package]] name = "pocket-ic" version = "3.0.0" @@ -1426,6 +3260,34 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1481,6 +3343,34 @@ dependencies = [ "unicode-width", ] +[[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 = "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.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.81" @@ -1500,7 +3390,7 @@ dependencies = [ "fnv", "lazy_static", "memchr", - "parking_lot", + "parking_lot 0.12.2", "protobuf", "thiserror", ] @@ -1529,6 +3419,17 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -1544,6 +3445,33 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rangemap" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" + +[[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_syscall" @@ -1627,7 +3555,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", + "http 1.1.0", "http-body", "http-body-util", "hyper", @@ -1662,6 +3590,42 @@ dependencies = [ "winreg", ] +[[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.8" @@ -1672,11 +3636,20 @@ dependencies = [ "cfg-if", "getrandom", "libc", - "spin", - "untrusted", + "spin 0.9.8", + "untrusted 0.9.0", "windows-sys 0.52.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 = "rstest" version = "0.18.2" @@ -1712,6 +3685,12 @@ 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_version" version = "0.4.0" @@ -1721,6 +3700,35 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.37.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes 1.0.11", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "itoa", + "libc", + "linux-raw-sys 0.4.14", + "once_cell", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" version = "0.22.4" @@ -1728,9 +3736,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", - "ring", + "ring 0.17.8", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.102.3", "subtle", "zeroize", ] @@ -1764,15 +3772,25 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + [[package]] name = "rustls-webpki" version = "0.102.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" dependencies = [ - "ring", + "ring 0.17.8", "rustls-pki-types", - "untrusted", + "untrusted 0.9.0", ] [[package]] @@ -1829,12 +3847,66 @@ dependencies = [ "syn 2.0.60", ] +[[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 = "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", + "pkcs8 0.9.0", + "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.9", + "generic-array", + "pkcs8 0.10.2", + "subtle", + "zeroize", +] + +[[package]] +name = "secret-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1da5c423b8783185fd3fecd1c8796c267d2c089d894ce5a93c280a5d3f780a2" +dependencies = [ + "aes 0.7.5", + "block-modes", + "hkdf", + "lazy_static", + "num", + "rand", + "serde", + "sha2 0.9.9", + "zbus", + "zbus_macros", + "zvariant", + "zvariant_derive", +] + [[package]] name = "security-framework" version = "2.10.0" @@ -1863,6 +3935,9 @@ name = "semver" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] [[package]] name = "serde" @@ -1925,6 +4000,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + [[package]] name = "serde_tokenstream" version = "0.1.7" @@ -1942,39 +4028,100 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[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.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", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", ] [[package]] -name = "sha2" -version = "0.10.8" +name = "signature" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "cfg-if", - "cpufeatures", - "digest", + "digest 0.10.7", + "rand_core", ] [[package]] -name = "sharded-slab" -version = "0.1.7" +name = "signature" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "lazy_static", + "digest 0.10.7", + "rand_core", ] [[package]] -name = "signal-hook-registry" -version = "1.4.2" +name = "simple_asn1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ - "libc", + "num-bigint 0.4.5", + "num-traits", + "thiserror", + "time", ] [[package]] @@ -1992,6 +4139,37 @@ dependencies = [ "autocfg", ] +[[package]] +name = "slog" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" + +[[package]] +name = "slog-async" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c8038f898a2c79507940990f05386455b3a317d8f18d4caea7cbc3d5096b84" +dependencies = [ + "crossbeam-channel", + "slog", + "take_mut", + "thread_local", +] + +[[package]] +name = "slog-term" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6e022d0b998abfe5c3782c1f03551a596269450ccd677ea51c56f8b214610e8" +dependencies = [ + "is-terminal", + "slog", + "term", + "thread_local", + "time", +] + [[package]] name = "slotmap" version = "1.0.7" @@ -2007,6 +4185,16 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[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.7" @@ -2017,12 +4205,38 @@ dependencies = [ "windows-sys 0.52.0", ] +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der 0.7.9", +] + [[package]] name = "stacker" version = "0.1.15" @@ -2036,6 +4250,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "station" version = "0.0.2-alpha.3" @@ -2055,13 +4275,13 @@ dependencies = [ "ic-ledger-types", "ic-stable-structures", "lazy_static", - "num-bigint", + "num-bigint 0.4.5", "orbit-essentials", "rstest", "serde", "serde_bytes", "serde_cbor", - "sha2", + "sha2 0.10.8", "station-api", "thiserror", "tokio", @@ -2087,17 +4307,54 @@ checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ "new_debug_unreachable", "once_cell", - "parking_lot", + "parking_lot 0.12.2", "phf_shared", "precomputed-hash", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" + +[[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", + "quote", + "rustversion", + "syn 1.0.109", +] + [[package]] name = "subtle" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + [[package]] name = "syn" version = "1.0.109" @@ -2126,6 +4383,35 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "take_mut" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" + +[[package]] +name = "tar" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand 2.1.0", + "rustix 0.38.34", + "windows-sys 0.52.0", +] + [[package]] name = "term" version = "0.7.0" @@ -2213,6 +4499,25 @@ dependencies = [ "time-core", ] +[[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", + "rustc-hash", + "sha2 0.10.8", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -2248,10 +4553,10 @@ dependencies = [ "libc", "mio", "num_cpus", - "parking_lot", + "parking_lot 0.12.2", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.7", "tokio-macros", "windows-sys 0.48.0", ] @@ -2304,6 +4609,32 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + [[package]] name = "tower" version = "0.4.13" @@ -2437,6 +4768,21 @@ 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 = "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.15" @@ -2476,6 +4822,22 @@ 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 = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -2499,7 +4861,7 @@ dependencies = [ "orbit-essentials", "serde", "serde_cbor", - "sha2", + "sha2 0.10.8", "thiserror", "tokio", "upgrader-api", @@ -2525,6 +4887,24 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "utf8-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.8.0" @@ -2547,6 +4927,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + [[package]] name = "walkdir" version = "2.5.0" @@ -2871,6 +5257,15 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.52.0" @@ -2881,8 +5276,124 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winx" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346" +dependencies = [ + "bitflags 2.5.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "xattr" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +dependencies = [ + "libc", + "linux-raw-sys 0.4.14", + "rustix 0.38.34", +] + +[[package]] +name = "zbus" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cbeb2291cd7267a94489b71376eda33496c1b9881adf6b36f26cc2779f3fc49" +dependencies = [ + "async-io", + "byteorder", + "derivative", + "enumflags2", + "fastrand 1.9.0", + "futures", + "nb-connect", + "nix", + "once_cell", + "polling", + "scoped-tls", + "serde", + "serde_repr", + "zbus_macros", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa3959a7847cf95e3d51e312856617c5b1b77191176c65a79a5f14d778bbe0a6" +dependencies = [ + "proc-macro-crate 0.1.5", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "zerocopy" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + [[package]] name = "zeroize" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "zvariant" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68c7b55f2074489b7e8e07d2d0a6ee6b4f233867a653c664d8020ba53692525" +dependencies = [ + "byteorder", + "enumflags2", + "libc", + "serde", + "static_assertions", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ca5e22593eb4212382d60d26350065bf2a02c34b85bc850474a74b589a3de9" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/Cargo.toml b/Cargo.toml index 48d98d56b..618fe8b2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,8 @@ members = [ "libs/orbit-essentials", "libs/orbit-essentials-macros", "libs/orbit-essentials-macros-tests", - "tools/dfx-orbit", "tests/integration", + "tools/dfx-orbit", ] [workspace.package] @@ -35,15 +35,23 @@ byteorder = "1.5" canbench-rs = "0.1.1" candid = "0.10.3" candid_parser = "0.1.3" +cap-std = "3.1.0" +clap = { version = "4.5.7", features = ["derive"] } +dfx-core = { git = "https://github.com/dfinity/sdk.git", tag = "0.20.2-beta.0" } convert_case = "0.6" futures = "0.3" getrandom = { version = "0.2", features = ["custom"] } hex = "0.4" +# The ic-agent matches the one sed by bthe +ic-agent = { git = "https://github.com/dfinity/agent-rs.git", rev = "8273d321e9a09fd8373bd4e38b0676ec6ad9c260" } +ic-asset = { git = "https://github.com/dfinity/sdk.git", rev = "ae10a96b381cfce3d8ac5a6cb940d19224ea6d2e" } +ic-certified-assets = { git = "https://github.com/dfinity/sdk.git", rev = "ae10a96b381cfce3d8ac5a6cb940d19224ea6d2e" } ic-cdk = "0.13.2" ic-cdk-macros = "0.9" ic-cdk-timers = "0.7.0" ic-ledger-types = "0.10.0" ic-stable-structures = "0.6.4" +ic-utils = { git = "https://github.com/dfinity/agent-rs.git", rev = "8273d321e9a09fd8373bd4e38b0676ec6ad9c260" } lazy_static = "1.4.0" mockall = "0.12.1" num-bigint = "0.4" @@ -61,6 +69,9 @@ serde_bytes = "0.11" serde_json = "1.0" serde_cbor = "0.11.2" sha2 = "0.10" +slog = "2.5.2" +slog-async = "2.4.0" +slog-term = "2.9.0" syn = { version = "2.0", features = ["extra-traits", "full"] } thiserror = "1.0.48" time = { version = "0.3", features = ["formatting", "parsing"] } diff --git a/tools/dfx-orbit/Cargo.toml b/tools/dfx-orbit/Cargo.toml index f64f1e4ff..e8241f772 100644 --- a/tools/dfx-orbit/Cargo.toml +++ b/tools/dfx-orbit/Cargo.toml @@ -11,3 +11,22 @@ license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow.workspace = true +candid.workspace = true +candid_parser.workspace = true +clap.workspace = true +serde.workspace = true +serde_json.workspace = true +cap-std.workspace = true +dfx-core.workspace = true +ic-agent.workspace = true +ic-asset.workspace = true +ic-certified-assets.workspace = true +ic-utils.workspace = true +slog.workspace = true +slog-term.workspace = true +slog-async.workspace = true +tokio.workspace = true + +orbit-station-api = { path = "../../core/station/api", package = "station-api" } +walkdir = "2.5.0" diff --git a/tools/dfx-orbit/src/args.rs b/tools/dfx-orbit/src/args.rs new file mode 100644 index 000000000..6d41c532f --- /dev/null +++ b/tools/dfx-orbit/src/args.rs @@ -0,0 +1,43 @@ +//! Command line interface for `dfx-orbit`. +pub mod canister; +pub mod dfx_extension_api; +pub mod request; +pub mod review; +pub mod station; + +use clap::{Parser, Subcommand}; +use station::StationArgs; + +/// Manages Orbit on the Internet Computer. +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +pub struct DfxOrbitArgs { + /// Manage Orbit stations. + #[command(subcommand)] + pub command: DfxOrbitSubcommands, +} + +/// CLI commands for managing Orbit on the Internet Computer. +#[derive(Debug, Subcommand)] +#[command(version, about, long_about = None)] +pub enum DfxOrbitSubcommands { + /// Manages Orbit stations. + #[command(subcommand)] + Station(StationArgs), + /// Manages external canisters with Orbit. + #[command(subcommand)] + Canister(canister::Args), + /// Make requests to Orbit + #[command(subcommand)] + Request(request::Args), + /// View and decide on requests. + #[command(subcommand)] + Review(review::Args), + /// Exercises the experimental DFX extension API. + /// + /// As the API is brand new and prototypical, this is exposed as a subcommand. Once stable it can be removed. + #[command(subcommand)] + DfxExtension(dfx_extension_api::Args), + /// Gets the caller's profile on an Orbit station. + Me, +} diff --git a/tools/dfx-orbit/src/args/canister.rs b/tools/dfx-orbit/src/args/canister.rs new file mode 100644 index 000000000..ea9b3f4f3 --- /dev/null +++ b/tools/dfx-orbit/src/args/canister.rs @@ -0,0 +1,38 @@ +//! dfx-orbit external canister management commands. + +use clap::{Parser, Subcommand}; +use std::fmt::Debug; + +/// Station management commands. +#[derive(Debug, Subcommand)] +#[command(version, about, long_about = None)] +pub enum Args { + /// Puts a canister controlled by the user under Orbit control. + Claim(Claim), + /// Uploads assets to an HTTP asset canister and requests that the assets be used. + UploadHttpAssets(UploadHttpAssets), +} + +/// Puts a canister controlled by the user under Orbit control. +#[derive(Debug, Parser)] +pub struct Claim { + /// The canister name or `canister_id`. + pub canister: String, + /// Make Orbit the exclusive controller of the canister. + #[clap(long, short, action)] + pub exclusive: bool, +} + +/// Uploads assets to an HTTP asset canister. +#[derive(Debug, Parser)] +pub struct UploadHttpAssets { + /// The canister name or `canister_id`. + #[structopt(long)] + pub canister: String, + /// A directory of assets to upload. + #[structopt(long)] + pub source: Vec, + /// Provide a running commentary. + #[arg(short, long, default_value_t = false)] + pub verbose: bool, +} diff --git a/tools/dfx-orbit/src/args/dfx_extension_api.rs b/tools/dfx-orbit/src/args/dfx_extension_api.rs new file mode 100644 index 000000000..fc399a099 --- /dev/null +++ b/tools/dfx-orbit/src/args/dfx_extension_api.rs @@ -0,0 +1,13 @@ +//! `dfx` does not have an extension API, yet. So imagine one existed and create a polyfill. This is it. +pub mod config; + +use clap::Subcommand; + +/// Station management commands. +#[derive(Debug, Subcommand)] +#[command(version, about, long_about = None)] +pub enum Args { + /// CLI subcommands for getting the local dfx configuration for this extension. + #[command(subcommand)] + Config(config::Args), +} diff --git a/tools/dfx-orbit/src/args/dfx_extension_api/config.rs b/tools/dfx-orbit/src/args/dfx_extension_api/config.rs new file mode 100644 index 000000000..89e63fbc5 --- /dev/null +++ b/tools/dfx-orbit/src/args/dfx_extension_api/config.rs @@ -0,0 +1,12 @@ +//! Access to the DFX extension configuration. +use clap::Subcommand; + +/// CLI subcommands for getting the local dfx configuration for this extension. +#[derive(Debug, Subcommand)] +#[command(version, about, long_about = None)] +pub enum Args { + /// Gets the local configuration directory. + Dir, + /// Gets the local configuration file. + File, +} diff --git a/tools/dfx-orbit/src/args/request.rs b/tools/dfx-orbit/src/args/request.rs new file mode 100644 index 000000000..a2b32f251 --- /dev/null +++ b/tools/dfx-orbit/src/args/request.rs @@ -0,0 +1,46 @@ +//! Defines the command line arguments for `dfx-orbit request`. These correspond to Orbit station `create_request` API calls. +pub mod canister; +pub mod permission; + +use clap::Subcommand; +use orbit_station_api::CreateRequestInput; + +use crate::orbit_station_agent::StationAgent; + +/// Request canister changes. +/// +// TODO: Add flags for --title, --summary, and --execution-plan. +// Note: I have looked at the docs and the anwer for how to do this really doesn't jump out at me. Google foo failed as well. Maybe the sdk repo has some examples. +#[derive(Debug, Subcommand)] +#[command(version, about, long_about = None)] +pub enum Args { + /// Request changes to a canister. + #[command(subcommand)] + Canister(canister::Args), + /// Request permissions. + #[command(subcommand)] + Permission(permission::Args), +} + +/// Converts the CLI arg type into the equivalent Orbit API type. +pub trait CreateRequestArgs { + /// Converts the CLI arg type into the equivalent Orbit API type. + fn into_create_request_input( + self, + station_agent: &StationAgent, + ) -> anyhow::Result; +} + +impl CreateRequestArgs for Args { + fn into_create_request_input( + self, + station_agent: &StationAgent, + ) -> anyhow::Result { + match self { + Args::Canister(canister_args) => canister_args.into_create_request_input(station_agent), + Args::Permission(permission_args) => { + permission_args.into_create_request_input(station_agent) + } + } + } +} diff --git a/tools/dfx-orbit/src/args/request/canister.rs b/tools/dfx-orbit/src/args/request/canister.rs new file mode 100644 index 000000000..682247c85 --- /dev/null +++ b/tools/dfx-orbit/src/args/request/canister.rs @@ -0,0 +1,32 @@ +//! CLI arguments for `dfx-orbit canister`. + +pub mod call; +pub mod change; + +use super::CreateRequestArgs; +use crate::orbit_station_agent::StationAgent; +use clap::Subcommand; + +/// Request canister changes. +#[derive(Debug, Subcommand)] +#[command(version, about, long_about = None)] +pub enum Args { + /// Request to update the canister. + #[command(subcommand)] + Change(change::Args), + /// Request to call a canister method. + Call(call::Args), +} + +impl CreateRequestArgs for Args { + /// Converts the CLI arg type into the equivalent Orbit API type. + fn into_create_request_input( + self, + station_agent: &StationAgent, + ) -> anyhow::Result { + match self { + Args::Change(change_args) => change_args.into_create_request_input(station_agent), + Args::Call(call_args) => call_args.into_create_request_input(station_agent), + } + } +} diff --git a/tools/dfx-orbit/src/args/request/canister/call.rs b/tools/dfx-orbit/src/args/request/canister/call.rs new file mode 100644 index 000000000..e759b68f6 --- /dev/null +++ b/tools/dfx-orbit/src/args/request/canister/call.rs @@ -0,0 +1,69 @@ +//! CLI arguments for `dfx-orbit canister call`. + +use anyhow::Context; +use clap::Parser; +use orbit_station_api::{CallExternalCanisterOperationInput, CanisterMethodDTO}; + +use crate::{args::request::CreateRequestArgs, orbit_station_agent::StationAgent}; + +/// Requests that a call be made to a canister. +#[derive(Debug, Parser)] +pub struct Args { + /// The canister name or ID. + canister: String, + /// The name of the method to call. + method_name: String, + /// The argument to pass to the method. + argument: Option, + // TODO: + // /// The format of the argument. + // #[clap(short, long)] + // r#type: Option, + // TODO: Read argument from a file + /// Specifies the amount of cycles to send on the call. + #[clap(short, long)] + with_cycles: Option, +} + +impl CreateRequestArgs for Args { + /// Converts the CLI arg type into the equivalent Orbit API type. + fn into_create_request_input( + self, + station_agent: &StationAgent, + ) -> anyhow::Result { + let Args { + canister, + method_name, + with_cycles, + argument, + } = self; + let canister_id = station_agent.canister_id(&canister)?; + // TODO: It would be really nice to be able to use `blob_from_arguments(..)` here, as in dfx, to geta ll the nice things such as help composing the argument. + let arg = if let Some(argument) = argument { + Some( + candid_parser::parse_idl_args(&argument) + .with_context(|| "Invalid Candid values".to_string())? + .to_bytes()?, + ) + } else { + None + }; + let operation = orbit_station_api::RequestOperationInput::CallExternalCanister( + CallExternalCanisterOperationInput { + validation_method: None, + execution_method: CanisterMethodDTO { + canister_id, + method_name, + }, + arg, + execution_method_cycles: with_cycles, + }, + ); + Ok(orbit_station_api::CreateRequestInput { + operation, + title: None, + summary: None, + execution_plan: None, + }) + } +} diff --git a/tools/dfx-orbit/src/args/request/canister/change.rs b/tools/dfx-orbit/src/args/request/canister/change.rs new file mode 100644 index 000000000..bcffc78ba --- /dev/null +++ b/tools/dfx-orbit/src/args/request/canister/change.rs @@ -0,0 +1,26 @@ +//! Arguments for `dfx orbit canister change`. +pub mod wasm; + +use clap::Subcommand; + +use crate::{args::request::CreateRequestArgs, orbit_station_agent::StationAgent}; + +/// Request permission. +#[derive(Debug, Subcommand)] +#[command(version, about, long_about = None)] +pub enum Args { + /// Request changes to the canister Wasm. + Wasm(wasm::Args), +} + +impl CreateRequestArgs for Args { + /// Converts the CLI arg type into the equivalent Orbit API type. + fn into_create_request_input( + self, + station_agent: &StationAgent, + ) -> anyhow::Result { + match self { + Args::Wasm(wasm_args) => wasm_args.into_create_request_input(station_agent), + } + } +} diff --git a/tools/dfx-orbit/src/args/request/canister/change/wasm.rs b/tools/dfx-orbit/src/args/request/canister/change/wasm.rs new file mode 100644 index 000000000..878d051ed --- /dev/null +++ b/tools/dfx-orbit/src/args/request/canister/change/wasm.rs @@ -0,0 +1,91 @@ +//! Arguments for `dfx orbit canister change wasm`. +use crate::{args::request::CreateRequestArgs, orbit_station_agent::StationAgent}; +use clap::{Parser, ValueEnum}; + +/// Requests that a canister be installed or updated. Equivalent to `orbit_station_api::CanisterInstallMode`. +#[derive(Debug, Parser)] +pub struct Args { + // TODO: Poll, waiting for the request to be accepted. + /// The canister name or ID. + #[clap(short, long)] + canister: String, + /// The installation mode. + #[clap(long, value_enum, rename_all = "kebab-case")] + mode: CanisterInstallMode, + /// The path to the Wasm file to install. + #[clap(short, long)] + wasm: String, + /// The argument to pass to the canister. + #[clap(short, long)] + arg: Option, + // TODO: exclusive OR + /// The path to a file containing the argument to pass to the canister. + #[clap(short = 'f', long)] + arg_file: Option, +} + +impl CreateRequestArgs for Args { + /// Converts the CLI arg type into the equivalent Orbit API type. + fn into_create_request_input( + self, + station_agent: &StationAgent, + ) -> anyhow::Result { + let Args { + canister, + mode, + wasm, + arg, + arg_file, + } = self; + let canister_id = station_agent.canister_id(&canister)?; + let operation = { + let module = std::fs::read(wasm) + .expect("Could not read Wasm file") + .to_vec(); + let arg = if let Some(file) = arg_file { + Some( + std::fs::read(file) + .expect("Could not read argument file") + .to_vec(), + ) + } else { + arg.map(|arg| arg.as_bytes().to_vec()) + }; + let mode = mode.into(); + orbit_station_api::ChangeExternalCanisterOperationInput { + canister_id, + mode, + module, + arg, + } + }; + let operation = orbit_station_api::RequestOperationInput::ChangeExternalCanister(operation); + Ok(orbit_station_api::CreateRequestInput { + operation, + title: None, + summary: None, + execution_plan: None, + }) + } +} + +/// Canister installation mode equivalent to `dfx canister install --mode XXX` and `orbit_station_api::CanisterInstallMode`. +#[derive(Copy, Clone, Eq, PartialEq, Debug, ValueEnum)] +pub enum CanisterInstallMode { + /// Corresponds to `dfx canister install` + Install, + /// Corresponds to `dfx canister reinstall` + Reinstall, + /// Corresponds to `dfx canister upgrade` + Upgrade, +} + +impl From for orbit_station_api::CanisterInstallMode { + fn from(mode: CanisterInstallMode) -> Self { + match mode { + CanisterInstallMode::Install => orbit_station_api::CanisterInstallMode::Install, + CanisterInstallMode::Reinstall => orbit_station_api::CanisterInstallMode::Reinstall, + CanisterInstallMode::Upgrade => orbit_station_api::CanisterInstallMode::Upgrade, + } + } +} diff --git a/tools/dfx-orbit/src/args/request/permission.rs b/tools/dfx-orbit/src/args/request/permission.rs new file mode 100644 index 000000000..a1caa0516 --- /dev/null +++ b/tools/dfx-orbit/src/args/request/permission.rs @@ -0,0 +1,37 @@ +//! Makes `EditPermission` requests to Orbit. +pub mod canister; +#[allow(clippy::module_inception)] +pub mod permission; + +use clap::Subcommand; + +use crate::orbit_station_agent::StationAgent; + +use super::CreateRequestArgs; + +/// Request permission. +#[derive(Debug, Subcommand)] +#[command(version, about, long_about = None)] +pub enum Args { + /// Request permission to create requests. + #[command(subcommand)] + Permission(permission::Args), + /// Request changes to canister permissions. + #[command(subcommand)] + Canister(canister::Args), +} + +impl CreateRequestArgs for Args { + /// Converts the CLI arg type into the equivalent Orbit API type. + fn into_create_request_input( + self, + station_agent: &StationAgent, + ) -> anyhow::Result { + match self { + Args::Canister(canister_args) => canister_args.into_create_request_input(station_agent), + Args::Permission(permission_args) => { + permission_args.into_create_request_input(station_agent) + } + } + } +} diff --git a/tools/dfx-orbit/src/args/request/permission/canister.rs b/tools/dfx-orbit/src/args/request/permission/canister.rs new file mode 100644 index 000000000..931abb245 --- /dev/null +++ b/tools/dfx-orbit/src/args/request/permission/canister.rs @@ -0,0 +1,26 @@ +//! Arguments for `dfx-orbit request permission canister`. +pub mod change; + +use clap::Subcommand; + +use crate::{args::request::CreateRequestArgs, orbit_station_agent::StationAgent}; + +/// Request canister changes. +#[derive(Debug, Subcommand)] +#[command(version, about, long_about = None)] +pub enum Args { + /// Request changes to canister permissions. + Change(change::Args), +} + +impl CreateRequestArgs for Args { + /// Converts the CLI arg type into the equivalent Orbit API type. + fn into_create_request_input( + self, + station_agent: &StationAgent, + ) -> anyhow::Result { + match self { + Args::Change(change_args) => change_args.into_create_request_input(station_agent), + } + } +} diff --git a/tools/dfx-orbit/src/args/request/permission/canister/change.rs b/tools/dfx-orbit/src/args/request/permission/canister/change.rs new file mode 100644 index 000000000..0d1557ede --- /dev/null +++ b/tools/dfx-orbit/src/args/request/permission/canister/change.rs @@ -0,0 +1,49 @@ +//! Arguments for `dfx-orbit request permission canister change`. +use clap::Parser; + +use crate::{args::request::CreateRequestArgs, orbit_station_agent::StationAgent}; + +/// Requests the privilige of proposing canister upgrades. +#[derive(Debug, Parser)] +pub struct Args { + /// Canister name or ID. + // TODO: If a canister is not specified, require --all. + #[structopt(long)] + pub canister: Option, +} + +impl CreateRequestArgs for Args { + /// Converts the CLI arg type into the equivalent Orbit API type. + fn into_create_request_input( + self, + station_agent: &StationAgent, + ) -> anyhow::Result { + let canisters: anyhow::Result = + if let Some(canister_name_or_id) = self.canister { + station_agent + .canister_id(&canister_name_or_id) + .map(orbit_station_api::ChangeExternalCanisterResourceTargetDTO::Canister) + } else { + Ok(orbit_station_api::ChangeExternalCanisterResourceTargetDTO::Any) + }; + + let resource = orbit_station_api::ResourceDTO::ExternalCanister( + orbit_station_api::ExternalCanisterResourceActionDTO::Change(canisters?), + ); + + let operation = orbit_station_api::RequestOperationInput::EditPermission( + orbit_station_api::EditPermissionOperationInput { + resource, + auth_scope: None, + users: None, + user_groups: None, + }, + ); + Ok(orbit_station_api::CreateRequestInput { + operation, + title: None, + summary: None, + execution_plan: None, + }) + } +} diff --git a/tools/dfx-orbit/src/args/request/permission/permission.rs b/tools/dfx-orbit/src/args/request/permission/permission.rs new file mode 100644 index 000000000..5ad3db917 --- /dev/null +++ b/tools/dfx-orbit/src/args/request/permission/permission.rs @@ -0,0 +1,33 @@ +//! Arguments for `dfx-orbit request permission permission`. +pub mod read; +pub mod update; + +use clap::Subcommand; +use orbit_station_api::CreateRequestInput; + +use crate::{args::request::CreateRequestArgs, orbit_station_agent::StationAgent}; + +/// Request canister changes. +/// +// TODO: Add flags for --title, --summary, and --execution-plan. +// Note: I have looked at the docs and the anwer for how to do this really doesn't jump out at me. Google foo failed as well. Maybe the sdk repo has some examples. +#[derive(Debug, Subcommand)] +#[command(version, about, long_about = None)] +pub enum Args { + /// Request permission to update permissions. + Update(update::Args), + /// Request permission to read permissions. + Read(read::Args), +} + +impl CreateRequestArgs for Args { + fn into_create_request_input( + self, + station_agent: &StationAgent, + ) -> anyhow::Result { + match self { + Args::Read(read_args) => read_args.into_create_request_input(station_agent), + Args::Update(update_args) => update_args.into_create_request_input(station_agent), + } + } +} diff --git a/tools/dfx-orbit/src/args/request/permission/permission/read.rs b/tools/dfx-orbit/src/args/request/permission/permission/read.rs new file mode 100644 index 000000000..a3ec19952 --- /dev/null +++ b/tools/dfx-orbit/src/args/request/permission/permission/read.rs @@ -0,0 +1,46 @@ +//! Arguments for `dfx-orbit request permission permission read`. +use clap::Parser; +use orbit_station_api::PermissionResourceActionDTO; + +use crate::{args::request::CreateRequestArgs, orbit_station_agent::StationAgent}; + +/// Requests the privilige of proposing canister upgrades. +#[derive(Debug, Parser)] +pub struct Args { + /// A users that should be permitted to change permissions. WARNING: Any user that is not listed will lose the ability to change permissions. + #[structopt(long)] + pub user: Vec, + /// A groups that should be permitted to change permissions. WARNING: Any group that is not listed will lose the ability to change permissions. + #[structopt(long)] + pub group: Vec, +} + +impl CreateRequestArgs for Args { + /// Converts the CLI arg type into the equivalent Orbit API type. + fn into_create_request_input( + self, + _station_agent: &StationAgent, + ) -> anyhow::Result { + let Args { + user: users, + group: user_groups, + } = self; + + let operation = orbit_station_api::RequestOperationInput::EditPermission( + orbit_station_api::EditPermissionOperationInput { + resource: orbit_station_api::ResourceDTO::Permission( + PermissionResourceActionDTO::Read, + ), + auth_scope: None, + users: Some(users), + user_groups: Some(user_groups), + }, + ); + Ok(orbit_station_api::CreateRequestInput { + operation, + title: None, + summary: None, + execution_plan: None, + }) + } +} diff --git a/tools/dfx-orbit/src/args/request/permission/permission/update.rs b/tools/dfx-orbit/src/args/request/permission/permission/update.rs new file mode 100644 index 000000000..977c949b6 --- /dev/null +++ b/tools/dfx-orbit/src/args/request/permission/permission/update.rs @@ -0,0 +1,46 @@ +//! Arguments for `dfx-orbit request permission permission update`. +use clap::Parser; +use orbit_station_api::PermissionResourceActionDTO; + +use crate::{args::request::CreateRequestArgs, orbit_station_agent::StationAgent}; + +/// Requests the privilige of proposing canister upgrades. +#[derive(Debug, Parser)] +pub struct Args { + /// A users that should be permitted to change permissions. WARNING: Any user that is not listed will lose the ability to change permissions. + #[structopt(long)] + pub user: Vec, + /// A groups that should be permitted to change permissions. WARNING: Any group that is not listed will lose the ability to change permissions. + #[structopt(long)] + pub group: Vec, +} + +impl CreateRequestArgs for Args { + /// Converts the CLI arg type into the equivalent Orbit API type. + fn into_create_request_input( + self, + _station_agent: &StationAgent, + ) -> anyhow::Result { + let Args { + user: users, + group: user_groups, + } = self; + + let operation = orbit_station_api::RequestOperationInput::EditPermission( + orbit_station_api::EditPermissionOperationInput { + resource: orbit_station_api::ResourceDTO::Permission( + PermissionResourceActionDTO::Update, + ), + auth_scope: None, + users: Some(users), + user_groups: Some(user_groups), + }, + ); + Ok(orbit_station_api::CreateRequestInput { + operation, + title: None, + summary: None, + execution_plan: None, + }) + } +} diff --git a/tools/dfx-orbit/src/args/review.rs b/tools/dfx-orbit/src/args/review.rs new file mode 100644 index 000000000..fc9c947e6 --- /dev/null +++ b/tools/dfx-orbit/src/args/review.rs @@ -0,0 +1,18 @@ +//! Defines the command line arguments for `dfx-orbit review`. These correspond to Orbit station `get_request`, `submit_request_approval` and related API calls. +pub mod id; +pub mod list; +pub mod next; + +use clap::Subcommand; + +/// Station management commands. +#[derive(Debug, Subcommand)] +#[command(version, about, long_about = None)] +pub enum Args { + /// List requests + List(list::Args), + /// Review the next request. + Next(next::Args), + /// Review a specific request. + Id(id::Args), +} diff --git a/tools/dfx-orbit/src/args/review/id.rs b/tools/dfx-orbit/src/args/review/id.rs new file mode 100644 index 000000000..059de5282 --- /dev/null +++ b/tools/dfx-orbit/src/args/review/id.rs @@ -0,0 +1,18 @@ +//! CLI arguments for `dfx-orbit review next`. + +use clap::Parser; +use orbit_station_api::GetRequestInput; + +/// Reviews the next request. +#[derive(Debug, Parser)] +pub struct Args { + /// The ID of the request to review. + request_id: String, +} + +impl From for GetRequestInput { + fn from(args: Args) -> Self { + let Args { request_id } = args; + GetRequestInput { request_id } + } +} diff --git a/tools/dfx-orbit/src/args/review/list.rs b/tools/dfx-orbit/src/args/review/list.rs new file mode 100644 index 000000000..e77c2da01 --- /dev/null +++ b/tools/dfx-orbit/src/args/review/list.rs @@ -0,0 +1,31 @@ +//! CLI arguments for `dfx-orbit review list`. + +use clap::Parser; + +/// Reviews the next request. +#[derive(Debug, Parser)] +pub struct Args { + /// Show only approvable requests. + #[clap(short, long)] + pub only_approvable: bool, +} + +impl From for orbit_station_api::ListRequestsInput { + fn from(args: Args) -> Self { + let Args { only_approvable } = args; + Self { + requester_ids: None, + approver_ids: None, + statuses: None, + operation_types: None, + expiration_from_dt: None, + expiration_to_dt: None, + created_from_dt: None, + created_to_dt: None, + paginate: None, + sort_by: None, + only_approvable, + with_evaluation_results: true, + } + } +} diff --git a/tools/dfx-orbit/src/args/review/next.rs b/tools/dfx-orbit/src/args/review/next.rs new file mode 100644 index 000000000..a623294c2 --- /dev/null +++ b/tools/dfx-orbit/src/args/review/next.rs @@ -0,0 +1,16 @@ +//! CLI arguments for `dfx-orbit review next`. + +use clap::Parser; + +/// Reviews the next request. +#[derive(Debug, Parser)] +pub struct Args {} + +impl From for orbit_station_api::GetNextApprovableRequestInput { + fn from(_: Args) -> Self { + Self { + excluded_request_ids: vec![], + operation_types: None, + } + } +} diff --git a/tools/dfx-orbit/src/args/station.rs b/tools/dfx-orbit/src/args/station.rs new file mode 100644 index 000000000..bdfc43432 --- /dev/null +++ b/tools/dfx-orbit/src/args/station.rs @@ -0,0 +1,107 @@ +//! dfx-orbit station management commands. +use std::fmt::{self, Display, Formatter}; + +use candid::Principal; +use clap::{Parser, Subcommand}; + +use crate::local_config::StationConfig; + +/// Station management commands. +#[derive(Debug, Subcommand)] +#[command(version, about, long_about = None)] +pub enum StationArgs { + /// Adds an Orbit station to the local dfx configuration. + Add(Add), + /// Lists Orbit stations in the local dfx configuration. + List(List), + /// The default station. + Default, + /// Sets the default station in the local dfx configuration. + Use(Use), + /// Shows the local configuration for an Orbit station. + Show(Show), + /// Renames an Orbit station in the local dfx configuration. + Rename(Rename), + /// Removes an Orbit station from the local dfx configuration. + Remove(Remove), +} + +/// Adds an Orbit station to the local dfx configuration. +#[derive(Debug, Parser)] +pub struct Add { + /// Wallet name. + pub name: String, + /// Station canister ID, called "Wallet ID" in the Orbit UI. + #[structopt(long)] + pub station_id: Principal, + /// The dfx network name. + #[structopt(long)] + pub network: String, + /// The Obit user interface URL. + #[structopt(long)] + pub url: String, +} + +impl From for StationConfig { + fn from(add: Add) -> Self { + Self { + name: add.name, + station_id: add.station_id.to_text(), + network: add.network, + url: add.url, + } + } +} + +/// Lists Orbit station in the local dfx configuration. +#[derive(Debug, Parser)] +pub struct List {} + +/// Response to a List command. +#[derive(Debug, serde::Serialize)] +pub struct ListResponse { + /// List of station names. + pub stations: Vec, +} +impl Display for ListResponse { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + for station in &self.stations { + writeln!(f, "{station}")?; + } + Ok(()) + } +} + +/// Shows the local configuration for an Orbit station. +#[derive(Debug, Parser)] +pub struct Show { + /// Station name. + #[structopt(long)] + pub name: Option, +} + +/// Renames an Orbit station in the local dfx configuration. +#[derive(Debug, Parser)] +pub struct Rename { + /// Station name. + #[structopt(long)] + pub old: String, + /// New station name. + #[structopt(long)] + pub new: String, +} + +/// Removes an Orbit station from the local dfx configuration. +#[derive(Debug, Parser)] +pub struct Remove { + /// Station name. + #[structopt(long)] + pub name: String, +} + +/// Sets the default station in the local dfx configuration. +#[derive(Debug, Parser)] +pub struct Use { + /// Station name. + pub name: String, +} diff --git a/tools/dfx-orbit/src/cli.rs b/tools/dfx-orbit/src/cli.rs new file mode 100644 index 000000000..7a8615138 --- /dev/null +++ b/tools/dfx-orbit/src/cli.rs @@ -0,0 +1,28 @@ +//! Implementation of the `dfx-orbit` commands. +pub mod canister; +pub mod dfx_extension_cli; +pub mod me; +pub mod request; +pub mod review; +pub mod station; + +use crate::args::{DfxOrbitArgs, DfxOrbitSubcommands}; +use anyhow::anyhow; + +/// A command line tool for interacting with Orbit on the Internet Computer. +pub async fn exec(args: DfxOrbitArgs) -> anyhow::Result<()> { + match args.command { + DfxOrbitSubcommands::Me => me::exec().await, + DfxOrbitSubcommands::Station(station_args) => station::exec(station_args), + DfxOrbitSubcommands::DfxExtension(dfx_extension_args) => { + dfx_extension_cli::exec(dfx_extension_args) + } + DfxOrbitSubcommands::Canister(canister_args) => canister::exec(canister_args).await, + DfxOrbitSubcommands::Request(request_args) => match request::exec(request_args).await { + Ok(Ok(_response)) => Ok(()), + Ok(Err(e)) => Err(anyhow!("Error response from the station: {e:?}")), + Err(e) => Err(e), + }, + DfxOrbitSubcommands::Review(review_args) => review::exec(review_args).await, + } +} diff --git a/tools/dfx-orbit/src/cli/canister.rs b/tools/dfx-orbit/src/cli/canister.rs new file mode 100644 index 000000000..8cfc0c5df --- /dev/null +++ b/tools/dfx-orbit/src/cli/canister.rs @@ -0,0 +1,15 @@ +//! Implements the `dfx-orbit canister *` CLI commands. +mod claim; +mod upload_http_assets; + +use crate::args::canister::Args; + +/// The main entry point for the `dfx orbit` CLI. +pub async fn exec(args: Args) -> anyhow::Result<()> { + match args { + Args::Claim(claim_args) => claim::exec(claim_args), + Args::UploadHttpAssets(upload_http_assets_args) => { + upload_http_assets::exec(upload_http_assets_args).await + } + } +} diff --git a/tools/dfx-orbit/src/cli/canister/claim.rs b/tools/dfx-orbit/src/cli/canister/claim.rs new file mode 100644 index 000000000..933cbf79e --- /dev/null +++ b/tools/dfx-orbit/src/cli/canister/claim.rs @@ -0,0 +1,35 @@ +//! Command to put a canister under Orbit control. +use anyhow::anyhow; + +use crate::{args::canister::Claim, dfx_extension_api, local_config}; + +/// Puts a canister controlled by the user under Orbit control. +// TODO: Need to be able to specify which Orbit to use, e.g. as a global flag. +// TODO: Implement this without calling the `dfx` executable. +pub fn exec(args: Claim) -> anyhow::Result<()> { + let Claim { + canister, + exclusive, + } = args; + let orbit_principal = &local_config::default_station()? + .ok_or_else(|| anyhow!("No default station specified"))? + .station_id; + let claim_type = if exclusive { + "--set-controller" + } else { + "--add-controller" + }; + let network = local_config::default_station()? + .ok_or_else(|| anyhow!("No default station specified"))? + .network; + dfx_extension_api::call_dfx_cli(vec![ + "canister", + "update-settings", + "--network", + &network, + claim_type, + orbit_principal, + &canister, + ])?; + Ok(()) +} diff --git a/tools/dfx-orbit/src/cli/canister/upload_http_assets.rs b/tools/dfx-orbit/src/cli/canister/upload_http_assets.rs new file mode 100644 index 000000000..8fb6219ea --- /dev/null +++ b/tools/dfx-orbit/src/cli/canister/upload_http_assets.rs @@ -0,0 +1,137 @@ +//! Implements the `dfx-orbit canister upload-http-assets` CLI command. +use ic_asset::canister_api::{ + methods::batch::compute_evidence, types::batch_upload::common::ComputeEvidenceArguments, +}; +use ic_utils::canister::CanisterBuilder; +use slog::{info, warn}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; +use walkdir::WalkDir; + +use crate::args::canister::UploadHttpAssets as Args; + +/// The main entry point for the `dfx orbit canister upload-http-assets` CLI. +pub async fn exec(args: Args) -> anyhow::Result<()> { + let Args { + canister, + source, + verbose: _verbose, + } = args; + // The path is needed in various forms. + let source_pathbufs: Vec = + source.iter().map(|source| PathBuf::from(&source)).collect(); + let source_paths: Vec<&Path> = source_pathbufs + .iter() + .map(|pathbuf| pathbuf.as_path()) + .collect(); + + let mut station_agent = crate::orbit_station_agent::StationAgent::new()?; + let canister_id = station_agent.canister_id(&canister)?; + let logger = station_agent.dfx.logger().clone(); + // Upload assets: + let canister_agent = CanisterBuilder::new() + .with_agent(station_agent.dfx.agent().await?) + .with_canister_id(canister_id) + .build()?; + let assets = assets_as_hash_map(&source); + let batch_id = ic_asset::upload_and_propose(&canister_agent, assets, &logger).await?; + println!("Proposed batch_id: {}", batch_id); + // Compute evidence locally: + let local_evidence = { + let local_evidence = + ic_asset::compute_evidence(&canister_agent, &source_paths, &logger).await?; + escape_hex_string(&local_evidence) + }; + // Wait for the canister to compute evidence: + let canister_evidence = { + // This part is stolen from ic_asset::sync::prepare_sync_for_proposal. Unfortunately the relevant functions are private. + // The docs explicitly include waiting for the evidence so this should really be made easier! See: https://github.com/dfinity/sdk/blob/2509e81e11e71dce4045c679686c952809525470/docs/design/asset-canister-interface.md?plain=1#L85 + let compute_evidence_arg = ComputeEvidenceArguments { + batch_id: batch_id.clone(), + max_iterations: Some(97), // 75% of max(130) = 97.5 + }; + info!(logger, "Computing evidence."); + let canister_evidence = loop { + if let Some(evidence) = compute_evidence(&canister_agent, &compute_evidence_arg).await? + { + break evidence; + } + }; + blob_from_bytes(&canister_evidence) + }; + + println!(r#"Proposed batch_id: {batch_id}"#); + if local_evidence == canister_evidence { + info!(logger, "Local evidence matches canister evidence."); + } else { + warn!(logger, "Local evidence does not match canister evidence:\n local: {local_evidence}\n canister:{canister_evidence}"); + } + println!(r#"Assets have been uploaded. For the changes to take effect, run:"#); + println!( + r#"dfx-orbit request canister call {canister} commit_proposed_batch '(record {{ batch_id = {batch_id} : nat; evidence = blob "{canister_evidence}" }})'"# + ); + Ok(()) +} + +/// Lists all the files at the given path. +/// +/// - Links are followed. +/// - Only files are returned. +/// - The files are sorted by name. +/// - Any files that cannot be read are ignored. +/// - The path includes the prefix. +fn list_assets(path: &str) -> Vec { + WalkDir::new(path) + .sort_by_file_name() + .follow_links(true) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|entry| entry.file_type().is_file()) + .map(|entry| entry.into_path()) + .collect() +} + +/// A hash map of all assets. +/// +/// Note: Given that ordering in a HashMap is not deterministic, is this really the best API? +fn assets_as_hash_map(asset_dirs: &[String]) -> HashMap { + asset_dirs + .iter() + .flat_map(|asset_dir| { + list_assets(asset_dir).into_iter().map(move |asset_path| { + let relative_path = asset_path.strip_prefix(asset_dir).expect( + "Internal error: list_assets should have returned only files in the asset_dir", + ); + let http_path = format!( + "/{relative_path}", + relative_path = relative_path.to_string_lossy() + ); + (http_path, asset_path) + }) + }) + .collect() +} + +/// Converts a hex string into one escaped as in a candid blob. +fn escape_hex_string(s: &str) -> String { + let mut ans = String::with_capacity(s.len() + s.len() / 2); + for chunk in s.chars().collect::>()[..].chunks(2) { + ans.push('\\'); + for char in chunk { + ans.push(*char); + } + } + ans +} + +/// Converts a byte array into one escaped as a candid blob +fn blob_from_bytes(bytes: &[u8]) -> String { + let mut ans = String::with_capacity(bytes.len() + bytes.len() / 2); + for byte in bytes { + ans.push('\\'); + ans.push_str(&format!("{:02x}", byte)); + } + ans +} diff --git a/tools/dfx-orbit/src/cli/dfx_extension_cli.rs b/tools/dfx-orbit/src/cli/dfx_extension_cli.rs new file mode 100644 index 000000000..22667bcc4 --- /dev/null +++ b/tools/dfx-orbit/src/cli/dfx_extension_cli.rs @@ -0,0 +1,27 @@ +//! Implements the dfx extension CLI commands +use crate::args::dfx_extension_api; +use std::io::Read; + +/// Implements CLI commands for getting data from the dfx extension API. +pub fn exec(dfx_extension_args: dfx_extension_api::Args) -> anyhow::Result<()> { + match dfx_extension_args { + dfx_extension_api::Args::Config(config_args) => match config_args { + dfx_extension_api::config::Args::Dir => { + let extension_agent = crate::dfx_extension_api::DfxExtensionAgent::new("orbit"); + let ans = extension_agent.extension_config_dir(); + println!("{ans:?}"); + } + dfx_extension_api::config::Args::File => { + let extension_agent = crate::dfx_extension_api::DfxExtensionAgent::new("orbit"); + let mut file = extension_agent + .extension_config_file() + .expect("Could not open file"); + let mut ans = String::new(); + file.read_to_string(&mut ans).expect("Could not read file"); + // let config: crate::local_config::CommonConfig = serde_json::from_reader(&mut file).unwrap(); + println!("{ans:?}"); + } + }, + } + Ok(()) +} diff --git a/tools/dfx-orbit/src/cli/me.rs b/tools/dfx-orbit/src/cli/me.rs new file mode 100644 index 000000000..725366a75 --- /dev/null +++ b/tools/dfx-orbit/src/cli/me.rs @@ -0,0 +1,30 @@ +//! Implementation of the `dfx-orbit me` command. + +use anyhow::Context; +use candid::encode_args; +use orbit_station_api::{ApiErrorDTO, MeResponse}; + +/// A command line tool for interacting with Orbit on the Internet Computer. +pub async fn exec() -> anyhow::Result<()> { + let mut station_agent = crate::orbit_station_agent::StationAgent::new()?; + let ans = station_agent + .update_orbit("me") + .await? + .with_arg(empty_args()) + .call_and_wait() + .await + .with_context(|| "Failed to make API call")?; + let ans: Result = + candid::decode_one(&ans).with_context(|| "Failed to decode response")?; + println!( + "{}", + serde_json::to_string_pretty(&ans) + .with_context(|| "Failed to serialize response as JSON")? + ); + Ok(()) +} + +/// Encodes an empty tuple as Candid. This is used for methods with no arguments. +pub fn empty_args() -> Vec { + encode_args(()).expect("Failed to candid encode empty tuple") +} diff --git a/tools/dfx-orbit/src/cli/request.rs b/tools/dfx-orbit/src/cli/request.rs new file mode 100644 index 000000000..f56f2b160 --- /dev/null +++ b/tools/dfx-orbit/src/cli/request.rs @@ -0,0 +1,30 @@ +//! Implements `dfx request` commands. These correspond to Orbit station `create_request` API calls. + +use crate::args::request::{Args, CreateRequestArgs}; +use orbit_station_api::{ApiErrorDTO, CreateRequestResponse}; + +/// The main entry point for the `dfx orbit request` CLI. +pub async fn exec(args: Args) -> anyhow::Result> { + let mut station_agent = crate::orbit_station_agent::StationAgent::new()?; + // Converts the CLI arg type into the equivalent Orbit API type. + let args = args.into_create_request_input(&station_agent)?; + // Makes an update call to the station. + let response_bytes = station_agent + .update_orbit("create_request") + .await? + .with_arg(candid::encode_one(args)?) + .call_and_wait() + .await?; + // Decodes the response from the station. + let ans: Result = candid::decode_one(&response_bytes)?; + if let Ok(response) = &ans { + let request_id = &response.request.id; + let request_url = station_agent.request_url(request_id); + println!("Created request: {request_id}"); + println!("Request URL: {request_url}"); + println!("To view the request, run: dfx-orbit review id {request_id}"); + } else { + println!("{ans:#?}"); + } + Ok(ans) +} diff --git a/tools/dfx-orbit/src/cli/review.rs b/tools/dfx-orbit/src/cli/review.rs new file mode 100644 index 000000000..e8ee0d2e3 --- /dev/null +++ b/tools/dfx-orbit/src/cli/review.rs @@ -0,0 +1,15 @@ +//! Implements `dfx review` commands. These correspond to Orbit station `get_request`, approve and related API calls. +pub mod id; +pub mod list; +pub mod next; + +use crate::args::review::Args; + +/// The main entry point for the `dfx orbit review` CLI. +pub async fn exec(args: Args) -> anyhow::Result<()> { + match args { + Args::Id(id_args) => id::exec(id_args).await, + Args::List(list_args) => list::exec(list_args).await, + Args::Next(next_args) => next::exec(next_args).await, + } +} diff --git a/tools/dfx-orbit/src/cli/review/id.rs b/tools/dfx-orbit/src/cli/review/id.rs new file mode 100644 index 000000000..3f68eb815 --- /dev/null +++ b/tools/dfx-orbit/src/cli/review/id.rs @@ -0,0 +1,27 @@ +//! Implements `dfx review id XXXX` command. This corresponds to Orbit station `get_request` API call. + +use anyhow::anyhow; +use candid::Principal; +use orbit_station_api::{ApiErrorDTO, GetRequestInput, GetRequestResponse}; + +use crate::args::review::id::Args; + +/// The main entry point for the `dfx orbit review next` CLI. +pub async fn exec(args: Args) -> anyhow::Result<()> { + let args = GetRequestInput::from(args); + let mut station_agent = crate::orbit_station_agent::StationAgent::new()?; + let ic_agent = station_agent.dfx.agent().await?; + // The station canister ID to which we will make the API call. + let orbit_canister_id = crate::local_config::default_station()? + .ok_or_else(|| anyhow!("No default station specified"))? + .station_id; + let canister_id = Principal::from_text(&orbit_canister_id)?; + let response_bytes = ic_agent + .update(&canister_id, "get_request") + .with_arg(candid::encode_one(args)?) + .call_and_wait() + .await?; + let ans: Result = candid::decode_one(&response_bytes)?; + println!("{ans:#?}"); + Ok(()) +} diff --git a/tools/dfx-orbit/src/cli/review/list.rs b/tools/dfx-orbit/src/cli/review/list.rs new file mode 100644 index 000000000..33e861d1f --- /dev/null +++ b/tools/dfx-orbit/src/cli/review/list.rs @@ -0,0 +1,20 @@ +//! Implements `dfx review list` command. These correspond to Orbit station `list_requests` API call. + +use orbit_station_api::{ApiErrorDTO, ListRequestsInput, ListRequestsResponse}; + +use crate::args::review::list::Args; + +/// The main entry point for the `dfx orbit review next` CLI. +pub async fn exec(args: Args) -> anyhow::Result<()> { + let args = ListRequestsInput::from(args); + let mut station_agent = crate::orbit_station_agent::StationAgent::new()?; + let response_bytes = station_agent + .update_orbit("list_requests") + .await? + .with_arg(candid::encode_one(args)?) + .call_and_wait() + .await?; + let ans: Result = candid::decode_one(&response_bytes)?; + println!("{}", serde_json::to_string_pretty(&ans)?); + Ok(()) +} diff --git a/tools/dfx-orbit/src/cli/review/next.rs b/tools/dfx-orbit/src/cli/review/next.rs new file mode 100644 index 000000000..e004ab729 --- /dev/null +++ b/tools/dfx-orbit/src/cli/review/next.rs @@ -0,0 +1,30 @@ +//! Implements `dfx review next` command. These correspond to Orbit station `get_next_approvable_request` API call. + +use anyhow::anyhow; +use candid::Principal; +use orbit_station_api::{ + ApiErrorDTO, GetNextApprovableRequestInput, GetNextApprovableRequestResponse, +}; + +use crate::args::review::next::Args; + +/// The main entry point for the `dfx orbit review next` CLI. +pub async fn exec(args: Args) -> anyhow::Result<()> { + let args = GetNextApprovableRequestInput::from(args); + let mut station_agent = crate::orbit_station_agent::StationAgent::new()?; + let ic_agent = station_agent.dfx.agent().await?; + // The station canister ID to which we will make the API call. + let orbit_canister_id = crate::local_config::default_station()? + .ok_or_else(|| anyhow!("No default station specified"))? + .station_id; + let canister_id = Principal::from_text(&orbit_canister_id)?; + let response_bytes = ic_agent + .update(&canister_id, "get_next_approvable_request") + .with_arg(candid::encode_one(args)?) + .call_and_wait() + .await?; + let ans: Result = + candid::decode_one(&response_bytes)?; + println!("{ans:#?}"); + Ok(()) +} diff --git a/tools/dfx-orbit/src/cli/station.rs b/tools/dfx-orbit/src/cli/station.rs new file mode 100644 index 000000000..506fb3489 --- /dev/null +++ b/tools/dfx-orbit/src/cli/station.rs @@ -0,0 +1,44 @@ +//! Implements the dfx extension CLI commands for managing stations. +use crate::args::station::{ListResponse, StationArgs}; +use crate::local_config; + +/// Implements CLI commands for managing Orbit stations. +pub fn exec(args: StationArgs) -> anyhow::Result<()> { + match args { + StationArgs::Add(add_args) => { + local_config::add_station(add_args).expect("Failed to add station to local dfx config"); + } + StationArgs::List(_list_args) => { + let stations = local_config::list_stations(); + let ans = ListResponse { stations }; + // Note: The formatted ans is a sequence of complete lines, so an additional newline, as provided by println, is not needed. + print!("{ans}"); + } + StationArgs::Default => { + let default_station = local_config::default_station_name() + .expect("Failed to get default station from local dfx config"); + if let Some(station) = default_station { + println!("{station}"); + } + } + StationArgs::Use(use_args) => { + local_config::set_default_station(Some(use_args.name)) + .expect("Failed to set default station in local dfx config"); + } + StationArgs::Show(show_args) => { + let station = local_config::station_or_default(show_args.name.as_deref()) + .expect("Failed to get station from local dfx config"); + let json = serde_json::to_string_pretty(&station).expect("Failed to serialize station"); + println!("{json}"); + } + StationArgs::Remove(remove_args) => { + local_config::remove_station(&remove_args.name) + .expect("Failed to remove station from local dfx config"); + } + StationArgs::Rename(rename_args) => { + local_config::rename_station(&rename_args.old, &rename_args.new) + .expect("Failed to rename station in local dfx config"); + } + } + Ok(()) +} diff --git a/tools/dfx-orbit/src/dfx_extension_api.rs b/tools/dfx-orbit/src/dfx_extension_api.rs new file mode 100644 index 000000000..6672dfa2a --- /dev/null +++ b/tools/dfx-orbit/src/dfx_extension_api.rs @@ -0,0 +1,185 @@ +//! Placeholders for the proposed dfx extension API methods. +use std::{ + process::{Command, Stdio}, + str::FromStr, +}; + +use anyhow::Context; +use candid::Principal; +use dfx_core::interface::dfx::DfxInterface; +use ic_agent::Agent; +use slog::{o, Drain, Logger}; + +use crate::local_config; + +/// Calls the dfx cli. +/// +/// Some methods are implemented as calls to the dfx cli until a library is available. +pub fn call_dfx_cli(args: Vec<&str>) -> anyhow::Result { + let output = Command::new("dfx") + .args(args) + // Tell the OS to record the command's output + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + // Execute the command, wait for it to complete, then capture the output + .output() + // Blow up if the OS was unable to start the program + .with_context(|| "Failed to call dfx. Is the dfx cli installed?")?; + + if output.status.success() { + Ok(String::from_utf8(output.stdout) + .context("Failed to parse dfx output as UTF-8")? + .trim() + .to_string()) + } else { + let stderr = String::from_utf8_lossy(&output.stderr); + Err(anyhow::anyhow!( + "dfx failed with status {}: {stderr}", + output.status + )) + } +} + +/// The API through which extensions SHOULD interact with ICP networks and dfx configuration. +pub struct DfxExtensionAgent { + /// The name of the dfx extension. + name: String, + /// The directory where all extension configuration files are stored, including those of other extensions. + extensions_dir: cap_std::fs::Dir, + /// An interface including an ic-agent. + dfx_interface: Option, + /// A logger; some public `sdk` repository methods require a specific type of logger so this is a compatible logger. + logger: Logger, +} + +impl DfxExtensionAgent { + /// Creates a new DfxExtensionAgent for the extension with the given name. + pub fn new(name: &str) -> Self { + let logger = { + let decorator = slog_term::TermDecorator::new().build(); + let drain = slog_term::FullFormat::new(decorator).build().fuse(); + let drain = slog_async::Async::new(drain).build().fuse(); + + slog::Logger::root(drain, o!()) + }; + Self { + name: name.to_string(), + extensions_dir: Self::extensions_dir() + .expect("Could not get the dfx extensions directory"), + dfx_interface: None, + logger, + } + } + + /// A logger; some public `sdk` repository methods require a specific type of logger so this is a compatible logger. + pub fn logger(&self) -> &Logger { + &self.logger + } + + /// Gets the extensions directory, typically at `~/.config/dfx/extensions` + fn extensions_dir() -> anyhow::Result { + let user_config_dir = dfx_core::config::directories::get_user_dfx_config_dir() + .with_context(|| "Could not find user dfx config dir")?; + let extensions_dir = user_config_dir.join("extensions"); + std::fs::create_dir_all(&extensions_dir).with_context(|| { + format!( + "Could not create directory at: {}", + extensions_dir.display() + ) + })?; + let std_dir = std::fs::File::open(&extensions_dir).with_context(|| { + format!("Could not open directory at: {}", extensions_dir.display()) + })?; + let cap_dir = cap_std::fs::Dir::from_std_file(std_dir); + Ok(cap_dir) + } + + /// Gets the basename of the extension config file. + fn config_file_name(&self) -> String { + format!("{}.json", &self.name) + } + + /// Gets the extension config file for this extension. If the file does not exist, it will be created. + /// + /// E.g. `~/.config/dfx/extensions/.json` + /// + /// Note: The file SHOULD be JSON but this is not enforced. + pub fn extension_config_file(&self) -> anyhow::Result { + let extension_config_dir = &self.extensions_dir; + let filename = self.config_file_name(); + let mut open_options = cap_std::fs::OpenOptions::new(); + let open_options = open_options.read(true).write(true).create(true); + extension_config_dir + .open_with(filename, open_options) + .with_context(|| { + format!( + "Could not create extension config file for extension: {}", + &self.name + ) + }) + } + + /// Gets the extension config directory for this extension. + pub fn extension_config_dir(&self) -> anyhow::Result { + let extensions_dir = &self.extensions_dir; + extensions_dir.create_dir_all(&self.name).with_context(|| { + format!( + "Could not create extension directory for extension: {}", + &self.name + ) + })?; + extensions_dir.open_dir(&self.name).with_context(|| { + format!( + "Could not open extension directory for extension: {}", + &self.name + ) + }) + } + + /// The name of the default dfx user identity. This is the identity given by `dfx identity whoami` (if any). + pub fn identity() -> anyhow::Result { + call_dfx_cli(vec!["identity", "whoami"]) + } + + /// Gets the dfx_core interface + pub async fn dfx_interface(&mut self) -> anyhow::Result<&DfxInterface> { + if self.dfx_interface.is_none() { + let network_name = local_config::station_or_default(None) + .with_context(|| "Failed to get station")? + .network; + let interface_builder = DfxInterface::builder().with_network_named(&network_name); + let interface = interface_builder.build().await?; + if !interface.network_descriptor().is_ic { + interface.agent().fetch_root_key().await?; + } + self.dfx_interface = Some(interface); + } + Ok(self + .dfx_interface + .as_ref() + .expect("Failed to get dfx interface")) + } + + /// Gets the dfx agent. + pub async fn agent(&mut self) -> anyhow::Result<&Agent> { + Ok(self.dfx_interface().await?.agent()) + } + + /// Gets a canister ID + // TODO: This is a bad API as the two names can be swapped and it will still compile. + pub fn canister_id( + &self, + canister_name: &str, + network_name: &str, + ) -> anyhow::Result { + let id = call_dfx_cli(vec![ + "canister", + "id", + "--network", + network_name, + canister_name, + ]) + .with_context(|| format!("Failed to look up canister '{canister_name}'"))?; + Principal::from_str(&id).with_context(|| format!("Could not parse canister ID: {}", id)) + } +} diff --git a/tools/dfx-orbit/src/lib.rs b/tools/dfx-orbit/src/lib.rs new file mode 100644 index 000000000..431154df4 --- /dev/null +++ b/tools/dfx-orbit/src/lib.rs @@ -0,0 +1,13 @@ +//! Library for interacting with Orbit on the Internet Computer. +#![warn(missing_docs)] +#![warn(clippy::missing_docs_in_private_items)] +#![deny(clippy::panic)] +#![deny(clippy::unwrap_used)] +pub mod args; +pub mod cli; +pub mod dfx_extension_api; +pub mod local_config; +pub mod orbit_station_agent; + +/// The name of the Orbit dfx extension. +pub const ORBIT_EXTENSION_NAME: &str = "orbit"; diff --git a/tools/dfx-orbit/src/local_config.rs b/tools/dfx-orbit/src/local_config.rs new file mode 100644 index 000000000..02a89d152 --- /dev/null +++ b/tools/dfx-orbit/src/local_config.rs @@ -0,0 +1,222 @@ +//! Local dfx configuration of Orbit stations. +use anyhow::Context; +use serde::{Deserialize, Serialize}; + +use crate::dfx_extension_api::DfxExtensionAgent; + +/// Configuration that lives in e.g. ~/.config/dfx/orbit.json +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct ExtensionConfig { + /// Default station name. + pub default_station: Option, +} + +/// Configuration for a given station that lives in e.g. ~/.config/dfx/orbit/stations/.json +#[derive(Debug, Serialize, Deserialize)] +pub struct StationConfig { + /// Station name. + pub name: String, + /// Wallet canister ID. + // TODO: This should be a principal. + pub station_id: String, + /// The dfx network name. + pub network: String, + /// The Orbit user interface URL. + // TODO: This would be better as URL. That requires serde to be implemented for URL. Consider: https://docs.rs/url_serde/latest/url_serde/ + pub url: String, +} + +/// The directoy in the orbit dfx config directory where stations are stored. +pub const STATIONS_DIR: &str = "stations"; +/// The directory in the orbit dfx config directory where stations are recorded. +pub fn stations_dir() -> anyhow::Result { + let dfx_extension_agent = DfxExtensionAgent::new("orbit"); + let config_dir = dfx_extension_agent + .extension_config_dir() + .expect("Failed to get extension config dir"); + config_dir.create_dir_all(STATIONS_DIR)?; + let stations_dir = config_dir + .open_dir(STATIONS_DIR) + .expect("Failed to open stations dir"); + Ok(stations_dir) +} +/// The name of the file in which the config for a given station is stored. +pub fn station_file_name(name: &str) -> String { + format!("{}.json", name) +} +/// The file in which the config for a particular station is stored. +/// +/// If the file does not exist, this will return an error. +pub fn station_file(name: &str) -> anyhow::Result { + open_station_file(name, false).with_context(|| { + format!("Failed to open station file for station '{name}': Is the station name correct?") + }) +} + +/// Creates and returne file in which the config for a particular station is stored. +/// +/// If the file already exists, this will return an error. +pub fn create_station_file(name: &str) -> anyhow::Result { + open_station_file(name, true).with_context(|| { + format!("Failed to create station file for station '{name}'. Does it already exist?") + }) +} + +/// The file in which the config for a particular station is stored. +/// +/// Optionally create the file if it does not exist. +pub fn open_station_file(name: &str, create_new: bool) -> anyhow::Result { + let basename = station_file_name(name); + let stations_dir = stations_dir()?; + let mut open_options = cap_std::fs::OpenOptions::new(); + let open_options = open_options.read(true).write(true).create_new(create_new); + let file = stations_dir.open_with(basename, open_options)?; + Ok(file) +} + +/// Lists all Orbit stations in the local dfx configuration. +pub fn list_stations() -> Vec { + // Get all entries in the station dir that are valid station configs. + let stations_dir = stations_dir().expect("Failed to get stations dir"); + stations_dir + .entries() + .expect("Failed to read stations dir") + // Filter out directory entries that could not be read. (Maybe we have no permissions to access the file or something like that?) + .filter_map(|entry| entry.ok()) + // Filter out entries that are not files. + .filter(|dir_entry| { + dir_entry + .file_type() + .expect("Failed to get file type") + .is_file() + }) + // Filter out entries that don't have the .json suffix. Return the filename without the suffix. This is the station name. + .filter_map(|dir_entry| { + dir_entry + .file_name() + .to_string_lossy() + .strip_suffix(".json") + .map(|name| name.to_string()) + }) + // Filter out entries that are not valid station configs. + .filter(|station_name| station(station_name).is_ok()) + .collect() +} + +/// Adds a new Orbit station to the local dfx configuration. +/// +/// If there is no default station, the new station is set as the default. +// TODO: Check that the URL works & is the root URL. +pub fn add_station(args: T) -> anyhow::Result<()> +where + T: Into, +{ + let station: StationConfig = args.into(); + let station_file = create_station_file(&station.name)?; + station_file.set_len(0)?; + serde_json::to_writer_pretty(station_file, &station).expect("Failed to write station file"); + + if default_station_name()?.is_none() { + set_default_station(Some(station.name.to_owned()))?; + } + + Ok(()) +} + +/// Gets the local stored dfx configuration for a given station, or the default station if none is specified. +pub fn station_or_default(name: Option<&str>) -> anyhow::Result { + if let Some(name) = name { + station(name) + } else { + let name = default_station() + .with_context(|| "Station not specified and failed to get default.")? + .with_context(|| "Station not specified and no default station set.")? + .name; + station(&name) + } +} + +/// Gets the local stored dfx configuration for a given station. +pub fn station(name: &str) -> anyhow::Result { + let station_file = station_file(name)?; + let station: StationConfig = + serde_json::from_reader(station_file).with_context(|| "Failed to parse station file")?; + Ok(station) +} + +/// Removes an Orbit station from the local dfx configuration. +pub fn remove_station(name: &str) -> anyhow::Result<()> { + let dir = stations_dir()?; + let path = station_file_name(name); + dir.remove_file(path) + .with_context(|| format!("Failed to remove dfx config file for station {}", name))?; + + if default_station_name()? == Some(name.to_string()) { + set_default_station(None)?; + } + Ok(()) +} + +/// Renames an Orbit station in the local dfx configuration. +/// +/// If the station being renamed is the default station, the default is updated to reflect the new name. +pub fn rename_station(name: &str, new_name: &str) -> anyhow::Result<()> { + let default_station_name = default_station_name()?; + let mut station = station(name)?; + station.name = new_name.to_string(); + add_station(station)?; + remove_station(name)?; + if default_station_name == Some(name.to_string()) { + set_default_station(Some(new_name.to_string()))?; + } + Ok(()) +} + +/// Gets the common configuration for this dfx extension. +/// +/// If the config does not exist or is empty, default values are assumed. +pub fn extension_config() -> anyhow::Result { + // TODO: Make orbit a const + let dfx_extension_agent = DfxExtensionAgent::new("orbit"); + let common_config_file = dfx_extension_agent.extension_config_file()?; + if common_config_file.metadata()?.len() == 0 { + Ok(ExtensionConfig::default()) + } else { + serde_json::from_reader(common_config_file) + .with_context(|| "Failed to parse extension config file as JSON.") + } +} + +/// Gets the name of the default Orbit station from the local dfx configuration. +pub fn default_station_name() -> anyhow::Result> { + Ok(extension_config()?.default_station) +} + +/// Gets the default Orbit station from the local dfx configuration. +pub fn default_station() -> anyhow::Result> { + if let Some(name) = default_station_name()? { + Ok(Some(station(&name)?)) + } else { + Ok(None) + } +} + +/// Sets the default Orbit station in the local dfx configuration. +pub fn set_default_station(name_maybe: Option) -> anyhow::Result<()> { + // Check if the station exists. + if let Some(name) = &name_maybe { + station(name)?; + } + // Set the default station. + let mut extension_config = extension_config()?; + extension_config.default_station = name_maybe; + let dfx_extension_agent = DfxExtensionAgent::new("orbit"); + let common_config_file = dfx_extension_agent.extension_config_file()?; + // TODO: Update atomically rather than rewriting. + // TODO: Have a dedicated function for doing the update rather than updating the file directly. + // Something like with_config_update(|config| { config.default_station = name_maybe; }) that provides the current config and writes the modified config back. + common_config_file.set_len(0)?; + serde_json::to_writer_pretty(common_config_file, &extension_config) + .with_context(|| "Failed to write extension config file as JSON.")?; + Ok(()) +} diff --git a/tools/dfx-orbit/src/main.rs b/tools/dfx-orbit/src/main.rs index e7a11a969..198125da8 100644 --- a/tools/dfx-orbit/src/main.rs +++ b/tools/dfx-orbit/src/main.rs @@ -1,3 +1,14 @@ +//! # `dfx orbit` tool for managing digital assets. +//! +//! Note: This will initially be a standalone executable, but will be converted into a dfx extension once the dfx subcommand extension framework is well defined. +use clap::Parser; +use dfx_orbit::{self as lib, args::DfxOrbitArgs}; +use tokio::runtime::Runtime; + fn main() { - println!("Hello, world!"); + let args = DfxOrbitArgs::parse(); + let runtime = Runtime::new().expect("Unable to create a runtime"); + runtime.block_on(async { + lib::cli::exec(args).await.unwrap(); + }); } diff --git a/tools/dfx-orbit/src/orbit_station_agent.rs b/tools/dfx-orbit/src/orbit_station_agent.rs new file mode 100644 index 000000000..c898b6f6d --- /dev/null +++ b/tools/dfx-orbit/src/orbit_station_agent.rs @@ -0,0 +1,98 @@ +//! A dfx and IC agent for communicating with an Orbit station. + +use anyhow::anyhow; +use candid::Principal; +use ic_agent::agent::UpdateBuilder; + +use crate::{ + dfx_extension_api::DfxExtensionAgent, + local_config::{self, StationConfig}, +}; + +/// A dfx agent for communicating with a specific station. +pub struct StationAgent { + /// The station to communicate with. + pub station: StationConfig, + /// The dfx agent. + pub dfx: DfxExtensionAgent, +} + +impl StationAgent { + /// Creates a new agent for communicating with the default station. + pub fn new() -> anyhow::Result { + let dfx = DfxExtensionAgent::new(crate::ORBIT_EXTENSION_NAME); + let station = local_config::default_station()? + .ok_or_else(|| anyhow::format_err!("No default station specified"))?; + Ok(Self { station, dfx }) + } + + /// Gets the ID of a given canister name. If the name is already an ID, it is returned as is. + pub fn canister_id(&self, canister_name: &str) -> anyhow::Result { + let network = &self.station.network; + self.dfx.canister_id(canister_name, network) + } + + /// Builds a canister update call on the network used by the station. + /// + /// # Example + /// ``` + /// let response_bytes = station_agent.update_canister(&canister_id, "method_name").await + /// .with_arg(candid::encode_one(args)?) + /// .call_and_wait() + /// .await?; + /// ``` + pub async fn update_canister_id( + &mut self, + canister_id: &Principal, + method_name: &str, + ) -> anyhow::Result { + Ok(self.dfx.agent().await?.update(canister_id, method_name)) + } + + /// Builds a canister update call to a named canister on the network used by the station. + /// + /// # Example + /// ``` + /// let response_bytes = station_agent.update_canister("mycanister", "method_name").await + /// .with_arg(candid::encode_one(args)?) + /// .call_and_wait() + /// .await?; + /// ``` + pub async fn update_canister( + &mut self, + canister: &str, + method_name: &str, + ) -> anyhow::Result { + let canister_id = self.canister_id(canister)?; + Ok(self.dfx.agent().await?.update(&canister_id, method_name)) + } + + /// Makes an update call to the station. + /// + /// # Example + /// ``` + /// let response_bytes = station_agent.update_canister("mycanister", "method_name").await + /// .with_arg(candid::encode_one(args)?) + /// .call_and_wait() + /// .await?; + /// ``` + pub async fn update_orbit(&mut self, method_name: &str) -> anyhow::Result { + let orbit_canister_id = crate::local_config::default_station()? + .ok_or_else(|| anyhow!("No default station specified"))? + .station_id; + let orbit_canister_id = Principal::from_text(&orbit_canister_id)?; + Ok(self + .dfx + .agent() + .await? + .update(&orbit_canister_id, method_name)) + } + + /// The URL for a request in the Orbit UI. + pub fn request_url(&self, request_id: &str) -> String { + format!( + "{}/en/settings/requests?reqid={}", + self.station.url, request_id + ) + } +}