From c529fe5acf7888aedf28d3065f68ecacf8610a97 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Tue, 16 Jun 2026 19:58:01 +0200 Subject: [PATCH] chore: migrate rust/threshold-ecdsa to icp-cli Replace dfx.json with icp.yaml (@dfinity/rust@v3.3.0), move src/ecdsa_example_rust/ to backend/, update Cargo.toml to workspace + backend package named "backend", migrate management canister ECDSA calls to ic-cdk-management-canister, update Makefile/test.sh/README for icp-cli, add rust-threshold-ecdsa CI job to threshold-ecdsa workflow, delete dfx.json, BUILD.md, .devcontainer/, and stale Cargo.lock. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/threshold-ecdsa.yml | 15 + .../.devcontainer/devcontainer.json | 20 - rust/threshold-ecdsa/BUILD.md | 113 --- rust/threshold-ecdsa/Cargo.lock | 707 ------------------ rust/threshold-ecdsa/Cargo.toml | 11 +- rust/threshold-ecdsa/Makefile | 33 +- rust/threshold-ecdsa/README.md | 87 +-- rust/threshold-ecdsa/backend/Cargo.toml | 19 + .../ecdsa_example_rust/src => backend}/lib.rs | 43 +- rust/threshold-ecdsa/dfx.json | 14 - rust/threshold-ecdsa/icp.yaml | 6 + rust/threshold-ecdsa/package.json | 6 +- .../src/ecdsa_example_rust/Cargo.toml | 19 - .../src/ecdsa_example_rust/ecdsa_example.did | 5 - rust/threshold-ecdsa/test.sh | 28 +- 15 files changed, 136 insertions(+), 990 deletions(-) delete mode 100644 rust/threshold-ecdsa/.devcontainer/devcontainer.json delete mode 100644 rust/threshold-ecdsa/BUILD.md delete mode 100644 rust/threshold-ecdsa/Cargo.lock create mode 100644 rust/threshold-ecdsa/backend/Cargo.toml rename rust/threshold-ecdsa/{src/ecdsa_example_rust/src => backend}/lib.rs (72%) delete mode 100644 rust/threshold-ecdsa/dfx.json create mode 100644 rust/threshold-ecdsa/icp.yaml delete mode 100644 rust/threshold-ecdsa/src/ecdsa_example_rust/Cargo.toml delete mode 100644 rust/threshold-ecdsa/src/ecdsa_example_rust/ecdsa_example.did mode change 100755 => 100644 rust/threshold-ecdsa/test.sh diff --git a/.github/workflows/threshold-ecdsa.yml b/.github/workflows/threshold-ecdsa.yml index 637524a84..eb9cdeb42 100644 --- a/.github/workflows/threshold-ecdsa.yml +++ b/.github/workflows/threshold-ecdsa.yml @@ -6,6 +6,7 @@ on: pull_request: paths: - motoko/threshold-ecdsa/** + - rust/threshold-ecdsa/** - .github/workflows/threshold-ecdsa.yml concurrency: @@ -26,3 +27,17 @@ jobs: icp network start -d icp deploy make test + + rust-threshold-ecdsa: + runs-on: ubuntu-24.04 + container: ghcr.io/dfinity/icp-dev-env-rust:1.0.0 + env: + ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Deploy and test + working-directory: rust/threshold-ecdsa + run: | + icp network start -d + icp deploy + make test diff --git a/rust/threshold-ecdsa/.devcontainer/devcontainer.json b/rust/threshold-ecdsa/.devcontainer/devcontainer.json deleted file mode 100644 index ebb0b8bcc..000000000 --- a/rust/threshold-ecdsa/.devcontainer/devcontainer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "ICP Dev Environment", - "image": "ghcr.io/dfinity/icp-dev-env-slim:22", - "forwardPorts": [4943, 5173], - "portsAttributes": { - "4943": { - "label": "dfx", - "onAutoForward": "ignore" - }, - "5173": { - "label": "vite", - "onAutoForward": "openBrowser" - } - }, - "customizations": { - "vscode": { - "extensions": ["dfinity-foundation.vscode-motoko"] - } - } -} diff --git a/rust/threshold-ecdsa/BUILD.md b/rust/threshold-ecdsa/BUILD.md deleted file mode 100644 index 24cfcb754..000000000 --- a/rust/threshold-ecdsa/BUILD.md +++ /dev/null @@ -1,113 +0,0 @@ -# Continue building locally - -Projects deployed through ICP Ninja are temporary; they will only be live for 20 minutes before they are removed. The command-line tool `dfx` can be used to continue building your ICP Ninja project locally and deploy it to the mainnet. - -To migrate your ICP Ninja project off of the web browser and develop it locally, follow these steps. - -### 1. Install developer tools. - -You can install the developer tools natively or use Dev Containers. - -#### Option 1: Natively install developer tools - -> Installing `dfx` natively is currently only supported on macOS and Linux systems. On Windows, it is recommended to use the Dev Containers option. - -1. Install `dfx` with the following command: - -``` - -sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)" - -``` - -> On Apple Silicon (e.g., Apple M1 chip), make sure you have Rosetta installed (`softwareupdate --install-rosetta`). - -2. [Install NodeJS](https://nodejs.org/en/download/package-manager). - -3. For Rust projects, you will also need to: - -- Install [Rust](https://doc.rust-lang.org/cargo/getting-started/installation.html#install-rust-and-cargo): `curl https://sh.rustup.rs -sSf | sh` - -- Install [candid-extractor](https://crates.io/crates/candid-extractor): `cargo install candid-extractor` - -4. For Motoko projects, you will also need to: - -- Install the Motoko package manager [Mops](https://docs.mops.one/quick-start#2-install-mops-cli): `npm i -g ic-mops` - -Lastly, navigate into your project's directory that you downloaded from ICP Ninja. - -#### Option 2: Dev Containers - -Continue building your projects locally by installing the [Dev Container extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) for VS Code and [Docker](https://docs.docker.com/engine/install/). - -Make sure Docker is running, then navigate into your project's directory that you downloaded from ICP Ninja and start the Dev Container by selecting `Dev-Containers: Reopen in Container` in VS Code's command palette (F1 or Ctrl/Cmd+Shift+P). - -> Note that local development ports (e.g. the ports used by `dfx` or `vite`) are forwarded from the Dev Container to your local machine. In the VS code terminal, use Ctrl/Cmd+Click on the displayed local URLs to open them in your browser. To view the current port mappings, click the "Ports" tab in the VS Code terminal window. - -### 2. Start the local development environment. - -``` -dfx start --background -``` - -### 3. Create a local developer identity. - -To manage your project's canisters, it is recommended that you create a local [developer identity](https://internetcomputer.org/docs/building-apps/getting-started/identities) rather than use the `dfx` default identity that is not stored securely. - -To create a new identity, run the commands: - -``` - -dfx identity new IDENTITY_NAME - -dfx identity use IDENTITY_NAME - -``` - -Replace `IDENTITY_NAME` with your preferred identity name. The first command `dfx start --background` starts the local `dfx` processes, then `dfx identity new` will create a new identity and return your identity's seed phase. Be sure to save this in a safe, secure location. - -The third command `dfx identity use` will tell `dfx` to use your new identity as the active identity. Any canister smart contracts created after running `dfx identity use` will be owned and controlled by the active identity. - -Your identity will have a principal ID associated with it. Principal IDs are used to identify different entities on ICP, such as users and canisters. - -[Learn more about ICP developer identities](https://internetcomputer.org/docs/building-apps/getting-started/identities). - -### 4. Deploy the project locally. - -Deploy your project to your local developer environment with: - -``` -npm install -dfx deploy - -``` - -Your project will be hosted on your local machine. The local canister URLs for your project will be shown in the terminal window as output of the `dfx deploy` command. You can open these URLs in your web browser to view the local instance of your project. - -### 5. Obtain cycles. - -To deploy your project to the mainnet for long-term public accessibility, first you will need [cycles](https://internetcomputer.org/docs/building-apps/getting-started/tokens-and-cycles). Cycles are used to pay for the resources your project uses on the mainnet, such as storage and compute. - -> This cost model is known as ICP's [reverse gas model](https://internetcomputer.org/docs/building-apps/essentials/gas-cost), where developers pay for their project's gas fees rather than users pay for their own gas fees. This model provides an enhanced end user experience since they do not need to hold tokens or sign transactions when using a dapp deployed on ICP. - -> Learn how much a project may cost by using the [pricing calculator](https://internetcomputer.org/docs/building-apps/essentials/cost-estimations-and-examples). - -Cycles can be obtained through [converting ICP tokens into cycles using `dfx`](https://internetcomputer.org/docs/building-apps/developer-tools/dfx/dfx-cycles#dfx-cycles-convert). - -### 6. Deploy to the mainnet. - -Once you have cycles, run the command: - -``` - -dfx deploy --network ic - -``` - -After your project has been deployed to the mainnet, it will continuously require cycles to pay for the resources it uses. You will need to [top up](https://internetcomputer.org/docs/building-apps/canister-management/topping-up) your project's canisters or set up automatic cycles management through a service such as [CycleOps](https://cycleops.dev/). - -> If your project's canisters run out of cycles, they will be removed from the network. - -## Additional examples - -Additional code examples and sample applications can be found in the [DFINITY examples repo](https://github.com/dfinity/examples). diff --git a/rust/threshold-ecdsa/Cargo.lock b/rust/threshold-ecdsa/Cargo.lock deleted file mode 100644 index 48a8e4037..000000000 --- a/rust/threshold-ecdsa/Cargo.lock +++ /dev/null @@ -1,707 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "anyhow" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "binread" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16598dfc8e6578e9b597d9910ba2e73618385dc9f4b1d43dd92c349d6be6418f" -dependencies = [ - "binread_derive", - "lazy_static", - "rustversion", -] - -[[package]] -name = "binread_derive" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d9672209df1714ee804b1f4d4f68c8eb2a90b1f7a07acf472f88ce198ef1fed" -dependencies = [ - "either", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "candid" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39be580be172631a35cac2fc6c765f365709de459edb88121b3e7a80cce6c1ec" -dependencies = [ - "anyhow", - "binread", - "byteorder", - "candid_derive", - "hex", - "ic_principal", - "leb128", - "num-bigint", - "num-traits", - "paste", - "pretty", - "serde", - "serde_bytes", - "stacker", - "thiserror", -] - -[[package]] -name = "candid_derive" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970c220da8aa2fa6f7ef5dbbf3ea5b620a59eb3ac107cfb95ae8c6eebdfb7a08" -dependencies = [ - "lazy_static", - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "cpufeatures" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "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-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "data-encoding" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" - -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - -[[package]] -name = "ecdsa" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12844141594ad74185a926d030f3b605f6a903b4e3fec351f3ea338ac5b7637e" -dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", -] - -[[package]] -name = "ecdsa_example" -version = "0.1.0" -dependencies = [ - "candid", - "getrandom", - "hex", - "ic-cdk", - "k256", - "serde", - "sha2", -] - -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "digest", - "ff", - "generic-array", - "group", - "pkcs8", - "rand_core", - "sec1", - "subtle", - "zeroize", -] - -[[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 = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "group" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" -dependencies = [ - "ff", - "rand_core", - "subtle", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "ic-cdk" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ec8231f413b8a4d74b99d7df26d6e917d6528a6245abde27f251210dcf9b72" -dependencies = [ - "candid", - "ic-cdk-macros", - "ic0", - "serde", - "serde_bytes", -] - -[[package]] -name = "ic-cdk-macros" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff30a6ddb3b50f1b7df689d203d2135b037706678368b1d73a9a98e17f87a9b4" -dependencies = [ - "candid", - "proc-macro2", - "quote", - "serde", - "serde_tokenstream", - "syn 1.0.109", -] - -[[package]] -name = "ic0" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a54b5297861c651551676e8c43df805dad175cc33bc97dbd992edbbb85dcbcdf" - -[[package]] -name = "ic_principal" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1762deb6f7c8d8c2bdee4b6c5a47b60195b74e9b5280faa5ba29692f8e17429c" -dependencies = [ - "crc32fast", - "data-encoding", - "serde", - "sha2", - "thiserror", -] - -[[package]] -name = "k256" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a55e0ff3b72c262bcf041d9e97f1b84492b68f1c1a384de2323d3dc9403397" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "once_cell", - "sha2", - "signature", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "leb128" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" - -[[package]] -name = "libc" -version = "0.2.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" - -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", - "serde", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pretty" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55c4d17d994b637e2f4daf6e5dc5d660d209d5642377d675d7a1c3ab69fa579" -dependencies = [ - "arrayvec", - "typed-arena", - "unicode-width", -] - -[[package]] -name = "proc-macro2" -version = "1.0.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "psm" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" -dependencies = [ - "cc", -] - -[[package]] -name = "quote" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint", - "hmac", - "zeroize", -] - -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "serde" -version = "1.0.195" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_bytes" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.195" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "serde_tokenstream" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "797ba1d80299b264f3aac68ab5d12e5825a561749db4df7cd7c8083900c5d4e9" -dependencies = [ - "proc-macro2", - "serde", - "syn 1.0.109", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "signature" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" -dependencies = [ - "digest", - "rand_core", -] - -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "stacker" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" -dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "winapi", -] - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "thiserror" -version = "1.0.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "typed-arena" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-width" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "zeroize" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/rust/threshold-ecdsa/Cargo.toml b/rust/threshold-ecdsa/Cargo.toml index 1542f0283..e5f39c67e 100644 --- a/rust/threshold-ecdsa/Cargo.toml +++ b/rust/threshold-ecdsa/Cargo.toml @@ -1,11 +1,4 @@ [workspace] -members = [ - "src/ecdsa_example_rust", -] - -[profile.release] -lto = true -opt-level = 'z' -panic = 'abort' - +members = ["backend"] +resolver = "2" diff --git a/rust/threshold-ecdsa/Makefile b/rust/threshold-ecdsa/Makefile index 3db31fe09..7571e10cb 100644 --- a/rust/threshold-ecdsa/Makefile +++ b/rust/threshold-ecdsa/Makefile @@ -1,21 +1,18 @@ -.PHONY: all -all: deploy +.PHONY: test -.PHONY: deploy -.SILENT: deploy -deploy: - dfx deploy +test: + @echo "=== Test 1/3: public_key() returns a hex-encoded public key ===" + @result=$$(icp canister call backend public_key '()') && \ + echo "$$result" && \ + echo "$$result" | grep -q 'public_key_hex' && \ + echo "PASS" || (echo "FAIL" && exit 1) -.PHONY: test -.SILENT: test -test: deploy - bash test.sh hello + @echo "=== Test 2/3: sign() returns a hex-encoded signature ===" + @result=$$(icp canister call backend sign '("hello world")') && \ + echo "$$result" && \ + echo "$$result" | grep -q 'signature_hex' && \ + echo "PASS" || (echo "FAIL" && exit 1) -.PHONY: clean -.SILENT: clean -clean: - rm -rf .dfx - rm -rf dist - rm -rf node_modules - rm -rf src/declarations - rm -f .env \ No newline at end of file + @echo "=== Test 3/3: signature verifies cryptographically (secp256k1) ===" + @npm install --silent + @chmod +x test.sh && ./test.sh "hello world" && echo "PASS" || (echo "FAIL" && exit 1) \ No newline at end of file diff --git a/rust/threshold-ecdsa/README.md b/rust/threshold-ecdsa/README.md index 93d71255b..286f51d13 100644 --- a/rust/threshold-ecdsa/README.md +++ b/rust/threshold-ecdsa/README.md @@ -1,58 +1,44 @@ -# Threshold ECDSA sample +# Threshold ECDSA -We present a minimal example canister smart contract for showcasing the [threshold ECDSA](https://internetcomputer.org/docs/building-apps/network-features/signatures/t-ecdsa) API. +This example demonstrates threshold ECDSA signing, part of ICP's [chain-key cryptography](https://internetcomputer.org/docs/building-apps/network-features/signatures/t-ecdsa). The canister acts as a signing oracle: callers can request a threshold ECDSA public key and sign arbitrary messages using the corresponding private key — without the canister ever holding the key material itself. -The example canister is a signing oracle that creates ECDSA signatures with keys derived from an input string. +See the [Motoko version](../../motoko/threshold-ecdsa) for a comparison. -More specifically: +## Build and deploy from the command line -- The sample canister receives a request that provides a message. -- The sample canister hashes the message and uses the key derivation string for the derivation path. -- The sample canister uses the above to request a signature from the threshold ECDSA [subnet](https://wiki.internetcomputer.org/wiki/Subnet_blockchain) (the threshold ECDSA is a subnet specializing in generating threshold ECDSA signatures). +### Prerequisites +- Node.js +- icp-cli: `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm` -## Deploying from ICP Ninja - -[![](https://icp.ninja/assets/open.svg)](https://icp.ninja/editor?g=https://github.com/dfinity/examples/tree/master/rust/threshold-ecdsa) - -### 1. Update source code with the right key ID. - -To deploy the sample code on ICP Ninja, the canister needs the right key ID for the right environment. Specifically, one needs to replace the value of the `key_id` in the `src/ecdsa_example_rust/src/lib.rs` file of the sample code. Before deploying to the mainnet from ICP Ninja, one should modify the code to use the right name of the `key_id`. - -There are three options: - -* `dfx_test_key`: a default key ID that is used in deploying to a local version of IC (via IC SDK). -* `test_key_1`: a master **test** key ID that is used in mainnet. -* `key_1`: a master **production** key ID that is used in mainnet. - -> [!WARNING] -> To deploy to IC mainnet, one needs to replace the value in `key_id `fields with the values `EcdsaKeyIds::TestKeyLocalDevelopment.to_key_id()` (mapping to `dfx_test_key`) to instead have either `EcdsaKeyIds::TestKey1.to_key_id()` (mapping to `test_key_1`) or `EcdsaKeyIds::ProductionKey1.to_key_id()` (mapping to `key_1`) depending on the desired intent. - -## Build and deploy from the command-line - -### 1. [Download and install the IC SDK.](https://internetcomputer.org/docs/building-apps/getting-started/install) +### Install +```bash +git clone https://github.com/dfinity/examples +cd examples/rust/threshold-ecdsa +``` -### 2. Download your project from ICP Ninja using the 'Download files' button on the upper left corner, or [clone the GitHub examples repository.](https://github.com/dfinity/examples/) +### Deploy and test +```bash +icp network start -d +icp deploy +make test +icp network stop +``` -### 3. Navigate into the project's directory. +## Key IDs -### 4. Deploy the project to your local environment: +The canister is configured with `KEY_ID = EcdsaKeyIds::TestKey1` by default (the master test key on mainnet). To use a different environment, update `KEY_ID` in `backend/lib.rs`: -``` -dfx start --background --clean && dfx deploy -``` +- `EcdsaKeyIds::TestKey1` — mainnet test key (`test_key_1`) +- `EcdsaKeyIds::ProductionKey1` — mainnet production key (`key_1`) +- `EcdsaKeyIds::TestKeyLocalDevelopment` — local development (`dfx_test_key`) ## Obtaining public keys -If you deployed your canister locally or to the mainnet, you should have a URL to the Candid web UI where you can access the public methods. You can call the `public-key` method. - -### Canister root public key - -For obtaining the canister's root public key, the derivation path in the API can be simply left empty. +Call `public_key()` to retrieve the ECDSA public key. The derivation path is left empty, so this returns the canister root key. ### Key derivation -- For obtaining a canister's public key below its root key in the BIP-32 key derivation hierarchy, a derivation path needs to be specified. As explained in the general documentation, each element in the array of the derivation path is either a 32-bit integer encoded as 4 bytes in big endian or a byte array of arbitrary length. The element is used to derive the key in the corresponding level at the derivation hierarchy. -- In the example code above, we use the bytes extracted from the `msg.caller` principal in the `derivation_path`, so that different callers of `public_key()` method of our canister will be able to get their own public keys. +To obtain a key below the root in the BIP-32 hierarchy, a derivation path must be specified. Each element in the derivation path array is either a 32-bit integer encoded as 4 bytes in big-endian, or a byte array of arbitrary length. Different derivation paths produce different keys — for example, passing the caller's principal bytes as a path element gives each caller a unique key. ## Signing @@ -60,24 +46,23 @@ Computing threshold ECDSA signatures is the core functionality of this feature. ## Signature verification -For completeness of the example, we show that the created signatures can be verified with the public key corresponding to the same canister and derivation path. - -The following shows how this verification can be done in Javascript, with the [secp256k1](https://www.npmjs.com/package/secp256k1) npm package: +The created signatures can be verified with the public key corresponding to the same canister and derivation path. Example verification in JavaScript using the [secp256k1](https://www.npmjs.com/package/secp256k1) npm package: ```javascript -let { ecdsaVerify } = require("secp256k1") +const { ecdsaVerify } = require("secp256k1"); +const crypto = require("crypto"); -let public_key = ... // Uint8Array type, the result of calling the above canister "public_key" function. -let hash = ... // 32-byte Uint8Array representing a binary hash (e.g. sha256). -let signature = ... // Uint8Array type, the result of calling the above canister "sign" function on `hash`. +const public_key = /* Uint8Array from public_key() */; +const message = "hello world"; +const message_hash = new Uint8Array(crypto.createHash("sha256").update(message, "utf-8").digest()); +const signature = /* Uint8Array from sign(message) */; -let verified = ecdsaVerify(signature, hash, public_key) +const verified = ecdsaVerify(signature, message_hash, public_key); +console.log("verified =", verified); // true ``` -The call to `ecdsaVerify` function should always return `true`. - Similar verifications can be done in many other languages with the help of cryptographic libraries that support the `secp256k1` curve. ## Security considerations and best practices -If you base your application on this example, it is recommended that you familiarize yourself with and adhere to the [security best practices](https://internetcomputer.org/docs/building-apps/security/overview) for developing on ICP. This example may not implement all the best practices. +Refer to the [security best practices](https://docs.internetcomputer.org/guides/security/overview) for information on security and best practices for your ICP app. diff --git a/rust/threshold-ecdsa/backend/Cargo.toml b/rust/threshold-ecdsa/backend/Cargo.toml new file mode 100644 index 000000000..2b76d18d9 --- /dev/null +++ b/rust/threshold-ecdsa/backend/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "backend" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] +path = "lib.rs" + +[dependencies] +candid = "0.10" +ic-cdk = "0.20" +ic-cdk-management-canister = "0.1.1" +hex = "0.4" +sha2 = "0.10" +k256 = { version = "0.13", features = ["ecdsa"] } +# Enable `custom` feature of k256's getrandom dependency. See lib.rs for more details. +getrandom = { version = "0.2", features = ["custom"] } +serde = "1" diff --git a/rust/threshold-ecdsa/src/ecdsa_example_rust/src/lib.rs b/rust/threshold-ecdsa/backend/lib.rs similarity index 72% rename from rust/threshold-ecdsa/src/ecdsa_example_rust/src/lib.rs rename to rust/threshold-ecdsa/backend/lib.rs index a0811ab73..f5bd5704b 100644 --- a/rust/threshold-ecdsa/src/ecdsa_example_rust/src/lib.rs +++ b/rust/threshold-ecdsa/backend/lib.rs @@ -1,9 +1,6 @@ use candid::CandidType; -use ic_cdk::api::management_canister::ecdsa::{ - ecdsa_public_key, sign_with_ecdsa, EcdsaCurve, EcdsaKeyId, EcdsaPublicKeyArgument, - SignWithEcdsaArgument, -}; use ic_cdk::{query, update}; +use ic_cdk_management_canister::{EcdsaCurve, EcdsaKeyId, EcdsaPublicKeyArgs, SignWithEcdsaArgs}; use serde::Serialize; use std::convert::TryFrom; @@ -22,19 +19,19 @@ struct SignatureVerificationReply { pub is_signature_valid: bool, } -const KEY_ID: EcdsaKeyIds = EcdsaKeyIds::TestKey1; // Use "ProductionKey1" for production and "TestKeyLocalDevelopment" for local development +// Use "test_key_1" for mainnet test key, "key_1" for mainnet production key, +// or "dfx_test_key" for local development. +const KEY_ID: EcdsaKeyIds = EcdsaKeyIds::TestKey1; #[update] async fn public_key() -> Result { - let request = EcdsaPublicKeyArgument { + let response = ic_cdk_management_canister::ecdsa_public_key(&EcdsaPublicKeyArgs { canister_id: None, derivation_path: vec![], key_id: KEY_ID.to_key_id(), - }; - - let (response,) = ecdsa_public_key(request) - .await - .map_err(|e| format!("ecdsa_public_key failed {}", e.1))?; + }) + .await + .map_err(|e| format!("ecdsa_public_key failed: {:?}", e))?; Ok(PublicKeyReply { public_key_hex: hex::encode(response.public_key), @@ -43,15 +40,13 @@ async fn public_key() -> Result { #[update] async fn sign(message: String) -> Result { - let request = SignWithEcdsaArgument { + let response = ic_cdk_management_canister::sign_with_ecdsa(&SignWithEcdsaArgs { message_hash: sha256(&message).to_vec(), derivation_path: vec![], key_id: KEY_ID.to_key_id(), - }; - - let (response,) = sign_with_ecdsa(request) - .await - .map_err(|e| format!("sign_with_ecdsa failed {}", e.1))?; + }) + .await + .map_err(|e| format!("sign_with_ecdsa failed: {:?}", e))?; Ok(SignatureReply { signature_hex: hex::encode(response.signature), @@ -59,27 +54,29 @@ async fn sign(message: String) -> Result { } #[query] -async fn verify( +fn verify( signature_hex: String, message: String, public_key_hex: String, ) -> Result { - let signature_bytes = hex::decode(signature_hex).expect("failed to hex-decode signature"); - let pubkey_bytes = hex::decode(public_key_hex).expect("failed to hex-decode public key"); + let signature_bytes = + hex::decode(&signature_hex).map_err(|e| format!("failed to hex-decode signature: {e}"))?; + let pubkey_bytes = hex::decode(&public_key_hex) + .map_err(|e| format!("failed to hex-decode public key: {e}"))?; let message_bytes = message.as_bytes(); use k256::ecdsa::signature::Verifier; let signature = k256::ecdsa::Signature::try_from(signature_bytes.as_slice()) - .expect("failed to deserialize signature"); + .map_err(|e| format!("failed to deserialize signature: {e}"))?; let is_signature_valid = k256::ecdsa::VerifyingKey::from_sec1_bytes(&pubkey_bytes) - .expect("failed to deserialize sec1 encoding into public key") + .map_err(|e| format!("failed to deserialize public key: {e}"))? .verify(message_bytes, &signature) .is_ok(); Ok(SignatureVerificationReply { is_signature_valid }) } -fn sha256(input: &String) -> [u8; 32] { +fn sha256(input: &str) -> [u8; 32] { use sha2::Digest; let mut hasher = sha2::Sha256::new(); hasher.update(input.as_bytes()); diff --git a/rust/threshold-ecdsa/dfx.json b/rust/threshold-ecdsa/dfx.json deleted file mode 100644 index 30f56c75b..000000000 --- a/rust/threshold-ecdsa/dfx.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "canisters": { - "ecdsa_example_rust": { - "candid": "src/ecdsa_example_rust/ecdsa_example.did", - "package": "ecdsa_example", - "type": "rust", - "metadata": [ - { - "name": "candid:service" - } - ] - } - } -} diff --git a/rust/threshold-ecdsa/icp.yaml b/rust/threshold-ecdsa/icp.yaml new file mode 100644 index 000000000..5b79cd933 --- /dev/null +++ b/rust/threshold-ecdsa/icp.yaml @@ -0,0 +1,6 @@ +canisters: + - name: backend + recipe: + type: "@dfinity/rust@v3.3.0" + configuration: + package: backend diff --git a/rust/threshold-ecdsa/package.json b/rust/threshold-ecdsa/package.json index 0e151140e..4b967204f 100644 --- a/rust/threshold-ecdsa/package.json +++ b/rust/threshold-ecdsa/package.json @@ -1,8 +1,8 @@ { "name": "threshold-ecdsa-test", - "version": "0.1.0", - "description": "Internet Computer starter application", + "version": "1.0.0", + "private": true, "dependencies": { - "secp256k1": "^4.0.3" + "secp256k1": "^4.0.4" } } diff --git a/rust/threshold-ecdsa/src/ecdsa_example_rust/Cargo.toml b/rust/threshold-ecdsa/src/ecdsa_example_rust/Cargo.toml deleted file mode 100644 index 755a5a800..000000000 --- a/rust/threshold-ecdsa/src/ecdsa_example_rust/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "ecdsa_example" -version = "0.1.0" -authors = ["paul@thev.net"] -edition = "2018" - -[lib] -path = "src/lib.rs" -crate-type = ["cdylib"] - -[dependencies] -serde = "1" -candid = "0.10.2" -ic-cdk = "0.12.0" -hex = "0.4.3" -sha2 = "0.10.6" -k256 = { version = "0.12.0", features = ["ecdsa"] } -# Enable `custom` feature of k256's getrandom dependency. See lib.rs for more details. -getrandom = { version = "0.2", features = ["custom"] } \ No newline at end of file diff --git a/rust/threshold-ecdsa/src/ecdsa_example_rust/ecdsa_example.did b/rust/threshold-ecdsa/src/ecdsa_example_rust/ecdsa_example.did deleted file mode 100644 index 99a3af889..000000000 --- a/rust/threshold-ecdsa/src/ecdsa_example_rust/ecdsa_example.did +++ /dev/null @@ -1,5 +0,0 @@ -service : { - public_key : () -> (variant { Ok: record { public_key_hex: text; }; Err: text }); - sign : (text) -> (variant { Ok: record { signature_hex: text; }; Err: text }); - verify : (text, text, text) -> (variant { Ok: record { is_signature_valid: bool; }; Err: text }); -} diff --git a/rust/threshold-ecdsa/test.sh b/rust/threshold-ecdsa/test.sh old mode 100755 new mode 100644 index 9b29480a4..e8410136e --- a/rust/threshold-ecdsa/test.sh +++ b/rust/threshold-ecdsa/test.sh @@ -1,19 +1,31 @@ #!/usr/bin/env bash +set -euo pipefail export LC_ALL=C + function get_text_in_double_quotes() { printf "$(echo "$1" | sed -e 's/^[^"]*"//' -e 's/".*//g')" } -test -z "$1" && echo "USAGE: $0 " && exit 1 +test -z "${1:-}" && echo "USAGE: $0 " && exit 1 message="$1" -echo message="$message" +echo "message=$message" + +signature_hex=$(get_text_in_double_quotes "$(icp canister call backend sign "(\"$message\")" | grep signature_hex)") +echo "signature_hex=$signature_hex" + +public_key_hex=$(get_text_in_double_quotes "$(icp canister call backend public_key '()' | grep public_key_hex)") +echo "public_key_hex=$public_key_hex" -signature_hex=$(get_text_in_double_quotes "$(dfx canister call ecdsa_example_rust sign "$message" | grep signature)") -echo signature_hex="$signature_hex" +node <