Skip to content

Commit 9206b22

Browse files
authored
chore(vetkeys): split basic_bls_signing into self-contained motoko + rust examples (#1444)
1 parent 0a51c0c commit 9206b22

36 files changed

Lines changed: 1049 additions & 226 deletions

.github/workflows/vetkeys-basic-bls-signing.yml

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ on:
66
- master
77
pull_request:
88
paths:
9+
- motoko/vetkeys/basic_bls_signing/**
910
- rust/vetkeys/basic_bls_signing/**
1011
- .github/workflows/vetkeys-basic-bls-signing.yml
1112

@@ -14,23 +15,28 @@ concurrency:
1415
cancel-in-progress: true
1516

1617
jobs:
17-
rust:
18+
motoko:
1819
runs-on: ubuntu-24.04
19-
container: ghcr.io/dfinity/icp-dev-env-rust:1.0.1
20+
container: ghcr.io/dfinity/icp-dev-env-motoko:1.0.1
2021
env:
2122
ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2223
steps:
2324
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
24-
- name: Deploy Basic Bls Signing Rust
25-
working-directory: rust/vetkeys/basic_bls_signing/rust
26-
run: icp network start -d && icp deploy
27-
motoko:
25+
- name: Deploy
26+
working-directory: motoko/vetkeys/basic_bls_signing
27+
run: |
28+
icp network start -d
29+
icp deploy
30+
31+
rust:
2832
runs-on: ubuntu-24.04
29-
container: ghcr.io/dfinity/icp-dev-env-motoko:1.0.1
33+
container: ghcr.io/dfinity/icp-dev-env-rust:1.0.1
3034
env:
3135
ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3236
steps:
3337
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
34-
- name: Deploy Basic Bls Signing Motoko
35-
working-directory: rust/vetkeys/basic_bls_signing/motoko
36-
run: icp network start -d && icp deploy
38+
- name: Deploy
39+
working-directory: rust/vetkeys/basic_bls_signing
40+
run: |
41+
icp network start -d
42+
icp deploy
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Threshold BLS Signatures (Motoko)
2+
3+
[View this sample's code on GitHub](https://github.com/dfinity/examples/tree/master/motoko/vetkeys/basic_bls_signing)
4+
5+
Also available in: [Rust](../../../rust/vetkeys/basic_bls_signing)
6+
7+
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.
8+
9+
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.
10+
11+
![UI Screenshot](ui_screenshot.png)
12+
13+
## Features
14+
15+
- **Signer Authorization**: Only authorized users can produce signatures, and only for their own identity.
16+
- **Frontend Signature Verification**: Any user can publish a signature from their principal in the canister storage, and the frontend automatically checks its validity.
17+
18+
## Build and deploy from the command line
19+
20+
### Prerequisites
21+
22+
- Install [Node.js](https://nodejs.org/en/download/)
23+
- Install [icp-cli](https://cli.internetcomputer.org): `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm`
24+
- Install [ic-mops](https://mops.one): `npm install -g ic-mops`
25+
26+
### (Optionally) choose a different master key
27+
28+
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.
29+
30+
### Install
31+
32+
```bash
33+
git clone https://github.com/dfinity/examples
34+
cd examples/motoko/vetkeys/basic_bls_signing
35+
```
36+
37+
### Deploy
38+
39+
```bash
40+
icp network start -d
41+
icp deploy
42+
```
43+
44+
Open the frontend URL printed by `icp deploy`.
45+
46+
To run the frontend in development mode with hot reloading (after `icp deploy`):
47+
48+
```bash
49+
npm run dev
50+
```
51+
52+
When done, stop the local network to free up the port for other projects:
53+
54+
```bash
55+
icp network stop
56+
```
57+
58+
## Example components
59+
60+
### Backend (`backend/`)
61+
62+
A single Motoko canister that:
63+
- Produces BLS signatures upon a user request.
64+
- Lets users retrieve the public key used to verify their signatures.
65+
- Lets users store signatures (real or fake) in a log data structure.
66+
67+
### Frontend (`frontend/`)
68+
69+
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.
70+
71+
## Updating the Candid interface
72+
73+
`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:
74+
75+
```bash
76+
mops generate candid backend
77+
```
78+
79+
## Additional resources
80+
81+
- **[What are VetKeys](https://docs.internetcomputer.org/concepts/vetkeys)** — more information about VetKeys and VetKD.
82+
- [Security best practices](https://docs.internetcomputer.org/guides/security/overview/)

rust/vetkeys/basic_bls_signing/motoko/backend/src/Main.mo renamed to motoko/vetkeys/basic_bls_signing/backend/app.mo

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Nat8 "mo:core/Nat8";
1010
import VetKeys "mo:ic-vetkeys";
1111
import Order "mo:core/Order";
1212

13-
shared persistent actor class (keyName : Text) = {
13+
actor class (keyName : Text) = {
1414
// Types
1515
type Signature = {
1616
message : Text;
@@ -36,7 +36,7 @@ shared persistent actor class (keyName : Text) = {
3636
}
3737
};
3838

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

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

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

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

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

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

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

112112
// Get verification key for the current caller
113-
public shared ({ caller }) func get_my_verification_key() : async Blob {
113+
public shared ({ caller }) func getMyVerificationKey() : async Blob {
114114
await VetKeys.ManagementCanister.blsPublicKey(
115115
null,
116116
context(caller),
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
type _anon_class_13_1 =
2+
service {
3+
getMySignatures: () -> (vec Signature) query;
4+
getMyVerificationKey: () -> (blob);
5+
signMessage: (message: text) -> (blob);
6+
};
7+
type Signature =
8+
record {
9+
message: text;
10+
signature: blob;
11+
timestamp: nat64;
12+
};
13+
service : (keyName: text) -> _anon_class_13_1
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules/
2+
dist/
3+
src/bindings/
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>VetKeys: Basic BLS Signing</title>
8+
</head>
9+
<body>
10+
<div id="app"></div>
11+
<script type="module" src="/src/main.ts"></script>
12+
</body>
13+
</html>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "frontend",
3+
"private": true,
4+
"type": "module",
5+
"scripts": {
6+
"prebuild": "npm i --include=dev",
7+
"build": "vite build",
8+
"dev": "vite"
9+
},
10+
"dependencies": {
11+
"@icp-sdk/auth": "^7.1.0",
12+
"@icp-sdk/core": "^5.4.0",
13+
"@icp-sdk/vetkeys": "^0.5.0-beta.0"
14+
},
15+
"devDependencies": {
16+
"@icp-sdk/bindgen": "~0.2.2",
17+
"typescript": "~5.7.2",
18+
"vite": "^6.4.1"
19+
}
20+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[
2+
{
3+
match: "**/*",
4+
security_policy: "hardened",
5+
headers: {
6+
"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;",
7+
},
8+
allow_raw_access: false
9+
},
10+
]

0 commit comments

Comments
 (0)