diff --git a/.github/workflows/vetkeys-basic-timelock-ibe.yml b/.github/workflows/vetkeys-basic-timelock-ibe.yml new file mode 100644 index 000000000..941cd3606 --- /dev/null +++ b/.github/workflows/vetkeys-basic-timelock-ibe.yml @@ -0,0 +1,26 @@ +name: vetkeys-basic-timelock-ibe + +on: + push: + branches: + - master + pull_request: + paths: + - rust/vetkeys/basic_timelock_ibe/** + - .github/workflows/vetkeys-basic-timelock-ibe.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + rust: + runs-on: ubuntu-24.04 + container: ghcr.io/dfinity/icp-dev-env-rust:0.1.0 + env: + ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Deploy Basic Timelock Ibe Rust + working-directory: rust/vetkeys/basic_timelock_ibe + run: icp network start -d && icp deploy diff --git a/rust/vetkeys/basic_timelock_ibe/README.md b/rust/vetkeys/basic_timelock_ibe/README.md index e723b3e7c..432448540 100644 --- a/rust/vetkeys/basic_timelock_ibe/README.md +++ b/rust/vetkeys/basic_timelock_ibe/README.md @@ -1,8 +1,10 @@ # Timelock Encryption + -The **Basic Timelock IBE** example demonstrates how to use **[VetKeys](https://internetcomputer.org/docs/building-apps/network-features/vetkeys/introduction)** to implement a secret-bid auction using timelock encryption on the **Internet Computer (IC)**. This application allows users authenticated with their **Internet Identity Principal** to create auction lots with a description and deadline, and other users to place a secret bid for the lot. The bids in this example are just dummy integer values, contrary to real-world use cases where users would place bids holding some value. +The **Basic Timelock IBE** example demonstrates how to use **[VetKeys](https://docs.internetcomputer.org/concepts/vetkeys)** to implement a secret-bid auction using timelock encryption on the **Internet Computer (IC)**. This application allows users authenticated with their **Internet Identity Principal** to create auction lots with a description and deadline, and other users to place a secret bid for the lot. The bids in this example are just dummy integer values, contrary to real-world use cases where users would place bids holding some value. This canister (IC smart contract) ensures that: 1. Only authorized users can create auction lots and place secret bids until the lot is closed. @@ -31,17 +33,39 @@ A canister functionality for decrypting secrets can be detected by inspecting th ### Prerequisites -- [Internet Computer software development kit](https://internetcomputer.org/docs/building-apps/getting-started/install) +- [ICP CLI](https://cli.internetcomputer.org) - [npm](https://www.npmjs.com/package/npm) ### (Optionally) Choose a Different Master Key -This example uses `test_key_1` by default. To use a different [available master key](https://internetcomputer.org/docs/building-apps/network-features/vetkeys/api#available-master-keys), change the `"init_arg": "(\"test_key_1\")"` line in `dfx.json` to the desired key before running `dfx deploy` in the next step. +This example uses `test_key_1` by default. To use a different [available master key](https://docs.internetcomputer.org/concepts/vetkeys/#api-overview), change the `init_args` value in `icp.yaml` to the desired key before running `icp deploy` in the next step. + +### Folder Structure + +This example has a single **Rust** backend with `icp.yaml` at the example root: + +``` +basic_timelock_ibe/ +├── frontend/ ← frontend +├── backend/ ← Rust canister source +└── icp.yaml ← deployment config +``` ### Deploy the Canisters Locally ```bash -dfx start --background && dfx deploy +icp network start -d && icp deploy +``` + +To run the frontend in development mode with hot reloading (after running `icp deploy`): +```bash +cd frontend +npm run dev:rust +``` + +When you are done testing, stop the local network to free up resources and unblock the default port for other projects: +```bash +icp network stop ``` ## Example Components @@ -61,13 +85,7 @@ The frontend is a vanilla typescript application providing a simple interface fo * Viewing open and closed lots including winners and bidders * Placing a secret bid for open lots created by other users -To run the frontend in development mode with hot reloading (after running `dfx deploy`): - -```bash -npm run dev -``` - ## Additional Resources - **[Basic IBE Example](../basic_ibe/)** - If you are interested in using IBE with users decrypting secrets. -- **[What are VetKeys](https://internetcomputer.org/docs/building-apps/network-features/vetkeys/introduction)** - For more information about VetKeys and VetKD. \ No newline at end of file +- **[What are VetKeys](https://docs.internetcomputer.org/concepts/vetkeys)** - For more information about VetKeys and VetKD. diff --git a/rust/vetkeys/basic_timelock_ibe/backend/Cargo.toml b/rust/vetkeys/basic_timelock_ibe/backend/Cargo.toml index 590cb7499..5874fd783 100644 --- a/rust/vetkeys/basic_timelock_ibe/backend/Cargo.toml +++ b/rust/vetkeys/basic_timelock_ibe/backend/Cargo.toml @@ -15,11 +15,12 @@ crate-type = ["cdylib"] [dependencies] candid = "0.10.2" getrandom = { version = "0.2", features = ["custom"]} -ic-cdk = "0.18.3" -ic-cdk-timers = "0.12.0" +ic-cdk = "0.20.1" +ic-cdk-management-canister = "0.1.1" +ic-cdk-timers = "1.0.0" ic-dummy-getrandom-for-wasm = "0.1.0" ic-stable-structures = "0.6.8" -ic-vetkeys = "0.3.0" +ic-vetkeys = "0.7.0" serde = "1.0.217" serde_bytes = "0.11.15" serde_cbor = "0.11.2" diff --git a/rust/vetkeys/basic_timelock_ibe/backend/Makefile b/rust/vetkeys/basic_timelock_ibe/backend/Makefile index bdaca5c38..703dba21a 100644 --- a/rust/vetkeys/basic_timelock_ibe/backend/Makefile +++ b/rust/vetkeys/basic_timelock_ibe/backend/Makefile @@ -12,4 +12,4 @@ extract-candid: compile-wasm .SILENT: clean clean: cargo clean - rm -rf ../.dfx \ No newline at end of file + rm -rf ../.icp \ No newline at end of file diff --git a/rust/vetkeys/basic_timelock_ibe/backend/backend.did b/rust/vetkeys/basic_timelock_ibe/backend/backend.did index 303e567f2..f336a0e99 100644 --- a/rust/vetkeys/basic_timelock_ibe/backend/backend.did +++ b/rust/vetkeys/basic_timelock_ibe/backend/backend.did @@ -11,7 +11,14 @@ type LotInformation = record { end_time : nat64; start_time : nat64; }; -type LotStatus = variant { Open; ClosedWithWinner : principal; ClosedNoBids }; +type LotStatus = variant { + // The auction is still open + Open; + // The auction is closed and the winner is the principal in the tuple + ClosedWithWinner : principal; + // The auction is closed and no bids were made + ClosedNoBids; +}; type OpenLotsResponse = record { lots : vec LotInformation; bidders : vec vec principal; diff --git a/rust/vetkeys/basic_timelock_ibe/backend/src/lib.rs b/rust/vetkeys/basic_timelock_ibe/backend/src/lib.rs index 1d0596645..291d64afd 100644 --- a/rust/vetkeys/basic_timelock_ibe/backend/src/lib.rs +++ b/rust/vetkeys/basic_timelock_ibe/backend/src/lib.rs @@ -2,7 +2,7 @@ use crate::types::{ BidCounter, DecryptedBid, EncryptedBid, LotId, LotInformation, VetKeyPublicKey, }; use candid::Principal; -use ic_cdk::management_canister::{VetKDCurve, VetKDDeriveKeyArgs, VetKDKeyId, VetKDPublicKeyArgs}; +use ic_cdk_management_canister::{VetKDCurve, VetKDDeriveKeyArgs, VetKDKeyId, VetKDPublicKeyArgs}; use ic_cdk::{init, post_upgrade, query, update}; use ic_stable_structures::memory_manager::{MemoryId, MemoryManager, VirtualMemory}; use ic_stable_structures::{BTreeMap as StableBTreeMap, Cell as StableCell, DefaultMemoryImpl}; @@ -109,7 +109,7 @@ async fn get_ibe_public_key() -> VetKeyPublicKey { key_id: key_id(), }; - let result = ic_cdk::management_canister::vetkd_public_key(&request) + let result = ic_cdk_management_canister::vetkd_public_key(&request) .await .expect("call to vetkd_public_key failed"); @@ -213,9 +213,7 @@ fn place_bid(lot_id: u128, encrypted_amount: Vec) -> Result<(), String> { #[update(guard = "is_self_call")] fn start_lot_closing_timer_job_with_interval_secs(secs: u64) { let secs = std::time::Duration::from_secs(secs); - ic_cdk_timers::set_timer_interval(secs, || { - ic_cdk::futures::spawn(close_one_lot_if_any_is_open_and_expired()) - }); + ic_cdk_timers::set_timer_interval(secs, || close_one_lot_if_any_is_open_and_expired()); } async fn close_one_lot_if_any_is_open_and_expired() { @@ -272,7 +270,7 @@ async fn decrypt_ciphertexts( transport_public_key: transport_secret_key.public_key().to_vec(), }; - let result = ic_cdk::management_canister::vetkd_derive_key(&request) + let result = ic_cdk_management_canister::vetkd_derive_key(&request) .await .expect("call to vetkd_derive_key failed"); diff --git a/rust/vetkeys/basic_timelock_ibe/dfx.json b/rust/vetkeys/basic_timelock_ibe/dfx.json deleted file mode 100644 index 65e4c4397..000000000 --- a/rust/vetkeys/basic_timelock_ibe/dfx.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "canisters": { - "basic_timelock_ibe": { - "candid": "backend/backend.did", - "package": "ic-vetkd-example-basic-timelock-ibe-backend", - "type": "rust", - "init_arg": "(\"test_key_1\")", - "metadata": [ - { - "name": "candid:service", - "visibility": "public" - } - ] - }, - "internet-identity": { - "candid": "https://github.com/dfinity/internet-identity/releases/download/release-2026-03-16/internet_identity.did", - "type": "custom", - "specified_id": "rdmx6-jaaaa-aaaaa-aaadq-cai", - "remote": { - "id": { - "ic": "rdmx6-jaaaa-aaaaa-aaadq-cai" - } - }, - "wasm": "https://github.com/dfinity/internet-identity/releases/download/release-2026-03-16/internet_identity_dev.wasm.gz" - }, - "www": { - "dependencies": ["basic_timelock_ibe", "internet-identity"], - "build": ["cd frontend && npm i --include=dev && npm run build"], - "frontend": { - "entrypoint": "frontend/dist/index.html" - }, - "source": ["frontend/dist/"], - "type": "assets", - "output_env_file": "frontend/.env" - } - }, - "networks": { - "local": { - "bind": "127.0.0.1:8000", - "type": "ephemeral" - } - } -} diff --git a/rust/vetkeys/basic_timelock_ibe/frontend/package.json b/rust/vetkeys/basic_timelock_ibe/frontend/package.json index 45ea14ca9..399d5a642 100644 --- a/rust/vetkeys/basic_timelock_ibe/frontend/package.json +++ b/rust/vetkeys/basic_timelock_ibe/frontend/package.json @@ -4,7 +4,8 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "npm run build:bindings && vite", + "dev": "npm run dev:rust", + "dev:rust": "npm run build:bindings && BACKEND=. vite", "build": "npm run build:bindings && tsc && vite build", "build:bindings": "cd scripts && ./gen_bindings.sh", "preview": "vite preview", @@ -20,13 +21,11 @@ "tslib": "^2.8.1", "typescript": "~5.7.2", "typescript-eslint": "^8.35.0", - "vite": "^6.4.1", - "vite-plugin-environment": "^1.1.3" + "vite": "^6.4.1" }, "dependencies": { - "@dfinity/agent": "^2.4.1", - "@dfinity/auth-client": "^2.4.1", - "@dfinity/principal": "^2.4.1", - "@dfinity/vetkeys": "^0.3.0" + "@icp-sdk/auth": "^7.1.0", + "@icp-sdk/core": "^5.4.0", + "@icp-sdk/vetkeys": "^0.5.0-beta.0" } } diff --git a/rust/vetkeys/basic_timelock_ibe/frontend/scripts/gen_bindings.sh b/rust/vetkeys/basic_timelock_ibe/frontend/scripts/gen_bindings.sh index be1cfcb90..3f66a7de8 100755 --- a/rust/vetkeys/basic_timelock_ibe/frontend/scripts/gen_bindings.sh +++ b/rust/vetkeys/basic_timelock_ibe/frontend/scripts/gen_bindings.sh @@ -1,8 +1,19 @@ #!/bin/bash +set -eu -cd ../../backend && make extract-candid && dfx generate basic_timelock_ibe && cd ../frontend && rm -r ./src/declarations >> /dev/null 2>&1 -mv ../src/declarations ./src && rmdir ../src +# Resolve the script's physical location so we work correctly even when +# frontend/ is reached via a symlink. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +FRONTEND_DIR="$(dirname "$SCRIPT_DIR")" +EXAMPLE_ROOT="$(dirname "$FRONTEND_DIR")" -# dfx 0.31+ generates @icp-sdk/core imports; rewrite to @dfinity/* to match deps -find ./src/declarations -type f \( -name '*.ts' -o -name '*.js' \) -exec \ - perl -i -pe 's|\@icp-sdk/core/agent|\@dfinity/agent|g; s|\@icp-sdk/core/principal|\@dfinity/principal|g; s|\@icp-sdk/core/candid|\@dfinity/candid|g' {} + \ No newline at end of file +if command -v candid-extractor >/dev/null 2>&1; then + (cd "$EXAMPLE_ROOT/backend" && make extract-candid) +fi + +rm -rf "$FRONTEND_DIR/src/declarations/basic_timelock_ibe" +mkdir -p "$FRONTEND_DIR/src/declarations/basic_timelock_ibe" +npx --yes @icp-sdk/bindgen \ + --did-file "$EXAMPLE_ROOT/backend/backend.did" \ + --out-dir "$FRONTEND_DIR/src/declarations/basic_timelock_ibe" \ + --declarations-flat --force diff --git a/rust/vetkeys/basic_timelock_ibe/frontend/src/main.ts b/rust/vetkeys/basic_timelock_ibe/frontend/src/main.ts index e5cd3a357..f04f8d636 100644 --- a/rust/vetkeys/basic_timelock_ibe/frontend/src/main.ts +++ b/rust/vetkeys/basic_timelock_ibe/frontend/src/main.ts @@ -1,74 +1,58 @@ -// Required to run `npm run dev`. -if (!window.global) { - window.global = window; -} - import "./style.css"; -import { createActor } from "./declarations/basic_timelock_ibe"; -import { Principal } from "@dfinity/principal"; +import { createActor, type Backend, type LotInformation } from "./declarations/basic_timelock_ibe/backend"; +import { Principal } from "@icp-sdk/core/principal"; import { DerivedPublicKey, IbeCiphertext, IbeIdentity, IbeSeed, -} from "@dfinity/vetkeys"; -import { - _SERVICE, - LotInformation, -} from "./declarations/basic_timelock_ibe/basic_timelock_ibe.did"; -import { AuthClient } from "@dfinity/auth-client"; -import type { ActorSubclass } from "@dfinity/agent"; +} from "@icp-sdk/vetkeys"; +import { AuthClient, LocalStorage } from "@icp-sdk/auth/client"; +import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env"; +import { HttpAgent } from "@icp-sdk/core/agent"; + +const canisterEnv = safeGetCanisterEnv<{ + "PUBLIC_CANISTER_ID:basic_timelock_ibe": string; +}>(); let ibePublicKey: DerivedPublicKey | undefined = undefined; let myPrincipal: Principal | undefined = undefined; let authClient: AuthClient | undefined; -let basicTimelockIbeCanister: ActorSubclass<_SERVICE> | undefined; +let basicTimelockIbeCanister: Backend | undefined; -function getBasicTimelockIbeCanister(): ActorSubclass<_SERVICE> { +async function getBasicTimelockIbeCanister(): Promise { if (basicTimelockIbeCanister) return basicTimelockIbeCanister; - if (!process.env.CANISTER_ID_BASIC_TIMELOCK_IBE) { - throw Error("CANISTER_ID_BASIC_TIMELOCK_IBE is not set"); - } + const canisterId = canisterEnv?.["PUBLIC_CANISTER_ID:basic_timelock_ibe"]; + if (!canisterId) throw Error("Canister ID for basic_timelock_ibe is not set"); if (!authClient) { throw Error("Auth client is not initialized"); } - const host = - process.env.DFX_NETWORK === "ic" - ? `https://${process.env.CANISTER_ID_BASIC_TIMELOCK_IBE}.ic0.app` - : "http://localhost:8000"; - - basicTimelockIbeCanister = createActor( - process.env.CANISTER_ID_BASIC_TIMELOCK_IBE, - { - agentOptions: { - identity: authClient.getIdentity(), - host, - }, - }, - ); - return basicTimelockIbeCanister!; + const agent = await HttpAgent.create({ + identity: await authClient.getIdentity(), + host: window.location.origin, + rootKey: canisterEnv?.IC_ROOT_KEY, + }); + + basicTimelockIbeCanister = createActor(canisterId, { agent }); + + return basicTimelockIbeCanister; } -export function login(client: AuthClient) { - void client.login({ - maxTimeToLive: BigInt(1800) * BigInt(1_000_000_000), - identityProvider: - process.env.DFX_NETWORK === "ic" - ? "https://identity.ic0.app/#authorize" - : `http://rdmx6-jaaaa-aaaaa-aaadq-cai.localhost:8000/#authorize`, - onSuccess: () => { - myPrincipal = client.getIdentity().getPrincipal(); - updateUI(true); - }, - onError: (error) => { - alert("Authentication failed: " + error); - }, - }); +export async function login(client: AuthClient): Promise { + try { + const identity = await client.signIn({ + maxTimeToLive: BigInt(1800) * BigInt(1_000_000_000), + }); + myPrincipal = identity.getPrincipal(); + updateUI(true); + } catch (error: unknown) { + alert("Authentication failed: " + error); + } } export function logout() { - void authClient?.logout(); + void authClient?.signOut(); myPrincipal = undefined; basicTimelockIbeCanister = undefined; updateUI(false); @@ -79,11 +63,19 @@ export function logout() { } async function initAuth() { - authClient = await AuthClient.create(); - const isAuthenticated = await authClient.isAuthenticated(); + const isLocalEnv = window.location.hostname === "localhost" || window.location.hostname.endsWith(".localhost"); + // Workaround for https://github.com/dfinity/icp-js-auth/issues/120 + // IdbStorage has a race condition on localhost dev servers. LocalStorage + // avoids IDB on local but uses plain string storage (less secure), so + // production deployments keep the default secure IdbStorage + ECDSA key. + authClient = new AuthClient({ + identityProvider: isLocalEnv ? "http://id.ai.localhost:8000/authorize" : "https://id.ai/authorize", + ...(isLocalEnv ? { storage: new LocalStorage(), keyType: "Ed25519" as const } : {}), + }); + const isAuthenticated = authClient.isAuthenticated(); if (isAuthenticated) { - myPrincipal = authClient.getIdentity().getPrincipal(); + myPrincipal = (await authClient.getIdentity()).getPrincipal(); updateUI(true); } else { updateUI(false); @@ -116,7 +108,7 @@ function handleLogin() { return; } - login(authClient); + void login(authClient); } document.querySelector("#app")!.innerHTML = ` @@ -186,7 +178,7 @@ async function getIbePublicKey(): Promise { if (ibePublicKey) return ibePublicKey; ibePublicKey = DerivedPublicKey.deserialize( new Uint8Array( - await getBasicTimelockIbeCanister().get_ibe_public_key(), + await (await getBasicTimelockIbeCanister()).get_ibe_public_key(), ), ); return ibePublicKey; @@ -211,7 +203,7 @@ async function createLot( description: string, durationSeconds: number, ) { - const result = await getBasicTimelockIbeCanister().create_lot( + const result = await (await getBasicTimelockIbeCanister()).create_lot( name, description, durationSeconds, @@ -303,7 +295,7 @@ function formatCountdown(endTime: bigint): string { async function listLots() { try { const [openLots, closedLots] = - await getBasicTimelockIbeCanister().get_lots(); + await (await getBasicTimelockIbeCanister()).get_lots(); const openLotsDiv = document.getElementById("openLots")!; const closedLotsDiv = document.getElementById("closedLots")!; @@ -450,7 +442,7 @@ async function placeBid(lotId: bigint, amount: number) { const encryptedAmount = await encrypt(amountBytes, lotIdBytes); // Place the bid - const result = await getBasicTimelockIbeCanister().place_bid( + const result = await (await getBasicTimelockIbeCanister()).place_bid( lotId, encryptedAmount, ); diff --git a/rust/vetkeys/basic_timelock_ibe/frontend/vite.config.ts b/rust/vetkeys/basic_timelock_ibe/frontend/vite.config.ts index 044e5ebf3..6af336e28 100644 --- a/rust/vetkeys/basic_timelock_ibe/frontend/vite.config.ts +++ b/rust/vetkeys/basic_timelock_ibe/frontend/vite.config.ts @@ -1,26 +1,51 @@ -import { defineConfig } from 'vite' -import typescript from '@rollup/plugin-typescript'; -import environment from 'vite-plugin-environment'; +import { defineConfig } from "vite"; +import { execSync } from "child_process"; -// https://vite.dev/config/ -export default defineConfig({ - plugins: [ - typescript({ - inlineSources: true, - }), - environment("all", { prefix: "CANISTER_" }), - environment("all", { prefix: "DFX_" }), - ], - build: { - sourcemap: true, - rollupOptions: { - output: { - inlineDynamicImports: true, - }, +const environment = process.env.ICP_ENVIRONMENT || "local"; +const CANISTER_NAMES = ["basic_timelock_ibe"]; + +function getDevServerConfig() { + const backend = process.env.BACKEND; + if (!backend) { + throw new Error( + "BACKEND env var is required. Use `npm run dev:rust`.", + ); + } + + const networkStatus = JSON.parse( + execSync(`icp network status -e ${environment} --json --project-root-override ../${backend}`, { + encoding: "utf-8", + }), + ); + const canisterParams = CANISTER_NAMES.map((name) => { + const id = execSync( + `icp canister status ${name} -e ${environment} --id-only --project-root-override ../${backend}`, + { encoding: "utf-8", stdio: "pipe" }, + ).trim(); + return `PUBLIC_CANISTER_ID:${name}=${id}`; + }).join("&"); + return { + headers: { + "Set-Cookie": `ic_env=${encodeURIComponent( + `${canisterParams}&ic_root_key=${networkStatus.root_key}`, + )}; SameSite=Lax;`, + }, + proxy: { + "/api": { target: networkStatus.api_url, changeOrigin: true }, + }, + hmr: false, + }; +} + +export default defineConfig(({ command }) => ({ + build: { + sourcemap: true, + rollupOptions: { + output: { + inlineDynamicImports: true, + }, + }, }, - }, - root: "./", - server: { - hmr: false - } -}) \ No newline at end of file + root: "./", + ...(command === "serve" ? { server: getDevServerConfig() } : {}), +})); diff --git a/rust/vetkeys/basic_timelock_ibe/icp.yaml b/rust/vetkeys/basic_timelock_ibe/icp.yaml new file mode 100644 index 000000000..405af4419 --- /dev/null +++ b/rust/vetkeys/basic_timelock_ibe/icp.yaml @@ -0,0 +1,23 @@ +canisters: + - name: basic_timelock_ibe + recipe: + type: "@dfinity/rust@v3.2.0" + configuration: + package: ic-vetkd-example-basic-timelock-ibe-backend + candid: backend/backend.did + init_args: + type: text + value: "(\"test_key_1\")" + + - name: www + recipe: + type: "@dfinity/asset-canister@v2.1.0" + configuration: + dir: dist + build: + - cd frontend && npm i --include=dev && npm run build && cd - && rm -rf dist; mv frontend/dist ./ + +networks: + - name: local + mode: managed + ii: true diff --git a/rust/vetkeys/basic_timelock_ibe/rust-toolchain.toml b/rust/vetkeys/basic_timelock_ibe/rust-toolchain.toml index 2a2058b04..6e5beca4f 100644 --- a/rust/vetkeys/basic_timelock_ibe/rust-toolchain.toml +++ b/rust/vetkeys/basic_timelock_ibe/rust-toolchain.toml @@ -1,4 +1,2 @@ [toolchain] -channel = "1.88.0" -targets = ["wasm32-unknown-unknown"] -profile = "default" \ No newline at end of file +targets = ["wasm32-unknown-unknown"] \ No newline at end of file