Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions .github/workflows/vetkeys-basic-bls-signing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
- master
pull_request:
paths:
- motoko/vetkeys/basic_bls_signing/**
- rust/vetkeys/basic_bls_signing/**
- .github/workflows/vetkeys-basic-bls-signing.yml

Expand All @@ -14,23 +15,28 @@ concurrency:
cancel-in-progress: true

jobs:
rust:
motoko:
runs-on: ubuntu-24.04
container: ghcr.io/dfinity/icp-dev-env-rust:1.0.1
container: ghcr.io/dfinity/icp-dev-env-motoko:1.0.1
env:
ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Deploy Basic Bls Signing Rust
working-directory: rust/vetkeys/basic_bls_signing/rust
run: icp network start -d && icp deploy
motoko:
- name: Deploy
working-directory: motoko/vetkeys/basic_bls_signing
run: |
icp network start -d
icp deploy
Comment thread
marc0olo marked this conversation as resolved.

rust:
runs-on: ubuntu-24.04
container: ghcr.io/dfinity/icp-dev-env-motoko:1.0.1
container: ghcr.io/dfinity/icp-dev-env-rust:1.0.1
env:
ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Deploy Basic Bls Signing Motoko
working-directory: rust/vetkeys/basic_bls_signing/motoko
run: icp network start -d && icp deploy
- name: Deploy
working-directory: rust/vetkeys/basic_bls_signing
run: |
icp network start -d
icp deploy
82 changes: 82 additions & 0 deletions motoko/vetkeys/basic_bls_signing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Threshold BLS Signatures (Motoko)

[View this sample's code on GitHub](https://github.com/dfinity/examples/tree/master/motoko/vetkeys/basic_bls_signing)

Also available in: [Rust](../../../rust/vetkeys/basic_bls_signing)

The **Basic BLS signing** example demonstrates how to use **[VetKeys](https://docs.internetcomputer.org/concepts/vetkeys)** to implement a threshold BLS signing service on the **Internet Computer (IC)**, where every authenticated user can ask the canister to produce signatures, with the **Internet Identity Principal** identifying the signer. The canister ensures a user can only produce signatures for their own principal, not for someone else's. Furthermore, the vetKeys in this app can only be produced upon a user request, as specified in the canister code — the canister cannot produce signatures for arbitrary users or messages.

To confirm the canister can only produce signatures in the intended way, users need to inspect the code installed in the canister. For this, it is crucial that canisters using VetKeys have their code public.

![UI Screenshot](ui_screenshot.png)

## Features

- **Signer Authorization**: Only authorized users can produce signatures, and only for their own identity.
- **Frontend Signature Verification**: Any user can publish a signature from their principal in the canister storage, and the frontend automatically checks its validity.

## Build and deploy from the command line

### Prerequisites

- Install [Node.js](https://nodejs.org/en/download/)
- Install [icp-cli](https://cli.internetcomputer.org): `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm`
- Install [ic-mops](https://mops.one): `npm install -g ic-mops`

### (Optionally) choose a different master key

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` before deploying.

### Install

```bash
git clone https://github.com/dfinity/examples
cd examples/motoko/vetkeys/basic_bls_signing
```

### Deploy

```bash
icp network start -d
icp deploy
```

Open the frontend URL printed by `icp deploy`.

To run the frontend in development mode with hot reloading (after `icp deploy`):

```bash
npm run dev
```

When done, stop the local network to free up the port for other projects:

```bash
icp network stop
```

## Example components

### Backend (`backend/`)

A single Motoko canister that:
- Produces BLS signatures upon a user request.
- Lets users retrieve the public key used to verify their signatures.
- Lets users store signatures (real or fake) in a log data structure.

### Frontend (`frontend/`)

A vanilla TypeScript application providing a simple interface for signing, showing the signatures stored in the canister, and verifying a signature. Canister bindings are generated from `backend/backend.did` at build time by the `@icp-sdk/bindgen` Vite plugin.

## Updating the Candid interface

`backend/backend.did` defines the backend's public interface; the frontend bindings are generated from it during the build. If you change the backend's public API, regenerate it:

```bash
mops generate candid backend
```

## Additional resources

- **[What are VetKeys](https://docs.internetcomputer.org/concepts/vetkeys)** — more information about VetKeys and VetKD.
- [Security best practices](https://docs.internetcomputer.org/guides/security/overview/)
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Nat8 "mo:core/Nat8";
import VetKeys "mo:ic-vetkeys";
import Order "mo:core/Order";

shared persistent actor class (keyName : Text) = {
actor class (keyName : Text) = {
// Types
type Signature = {
message : Text;
Expand All @@ -36,7 +36,7 @@ shared persistent actor class (keyName : Text) = {
}
};

// Stable storage for signatures
// Signatures are retained across upgrades: this actor field is not declared `transient`.
private var signatures = Map.empty<SignatureKey, Signature>();

// Helper function to get current timestamp
Expand All @@ -46,8 +46,8 @@ shared persistent actor class (keyName : Text) = {

// Helper function to create context for vetKD
private func context(signer : Principal) : Blob {
// Domain separator for this dapp
let domainSeparator : [Nat8] = Blob.toArray(Text.encodeUtf8("basic_bls_signing_dapp"));
// Domain separator for this app
let domainSeparator : [Nat8] = Blob.toArray(Text.encodeUtf8("basic_bls_signing_app"));
let domainSeparatorLength : [Nat8] = [Nat8.fromNat(domainSeparator.size())]; // Length of domain separator

// Combine domain separator length, domain separator, and signer principal
Expand All @@ -71,7 +71,7 @@ shared persistent actor class (keyName : Text) = {
};

// Sign a message using BLS
public shared ({ caller }) func sign_message(message : Text) : async Blob {
public shared ({ caller }) func signMessage(message : Text) : async Blob {
let signatureBytes = await VetKeys.ManagementCanister.signWithBls(
Text.encodeUtf8(message),
context(caller),
Expand All @@ -97,7 +97,7 @@ shared persistent actor class (keyName : Text) = {
};

// Get all signatures for the current caller
public shared query ({ caller }) func get_my_signatures() : async [Signature] {
public shared query ({ caller }) func getMySignatures() : async [Signature] {
var callerSignatures = List.empty<Signature>();

for ((key, value) in Map.entries(signatures)) {
Expand All @@ -110,7 +110,7 @@ shared persistent actor class (keyName : Text) = {
};

// Get verification key for the current caller
public shared ({ caller }) func get_my_verification_key() : async Blob {
public shared ({ caller }) func getMyVerificationKey() : async Blob {
await VetKeys.ManagementCanister.blsPublicKey(
null,
context(caller),
Expand Down
13 changes: 13 additions & 0 deletions motoko/vetkeys/basic_bls_signing/backend/backend.did
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
type _anon_class_13_1 =
service {
getMySignatures: () -> (vec Signature) query;
getMyVerificationKey: () -> (blob);
signMessage: (message: text) -> (blob);
};
type Signature =
record {
message: text;
signature: blob;
timestamp: nat64;
};
service : (keyName: text) -> _anon_class_13_1
3 changes: 3 additions & 0 deletions motoko/vetkeys/basic_bls_signing/frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
dist/
src/bindings/
13 changes: 13 additions & 0 deletions motoko/vetkeys/basic_bls_signing/frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>VetKeys: Basic BLS Signing</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
20 changes: 20 additions & 0 deletions motoko/vetkeys/basic_bls_signing/frontend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "frontend",
"private": true,
"type": "module",
"scripts": {
"prebuild": "npm i --include=dev",
"build": "vite build",
"dev": "vite"
},
"dependencies": {
"@icp-sdk/auth": "^7.1.0",
"@icp-sdk/core": "^5.4.0",
"@icp-sdk/vetkeys": "^0.5.0-beta.0"
},
"devDependencies": {
"@icp-sdk/bindgen": "~0.2.2",
"typescript": "~5.7.2",
"vite": "^6.4.1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
match: "**/*",
security_policy: "hardened",
headers: {
"Content-Security-Policy": "default-src 'self';script-src 'self';connect-src 'self' http://localhost:* https://icp0.io https://*.icp0.io https://icp-api.io;img-src 'self';object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';upgrade-insecure-requests;",
},
allow_raw_access: false
},
]
Loading
Loading