Skip to content

Commit 1adf9c4

Browse files
committed
feat: add test utils wasm package
add unit test for certificate verification JS library re #TT-296
1 parent 4686895 commit 1adf9c4

File tree

26 files changed

+3220
-131
lines changed

26 files changed

+3220
-131
lines changed

.github/actions/setup-dfx/action.yml

+1-5
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@ description: Setup DFX
44
runs:
55
using: 'composite'
66
steps:
7-
- name: Get DFX version
8-
shell: bash
9-
run: echo "dfx_version=$(cat dfx.json | jq -r .dfx)" >> "$GITHUB_ENV"
10-
117
- name: Cache DFX
128
uses: actions/cache@v3
139
with:
@@ -22,7 +18,7 @@ runs:
2218
echo "DFX restored from cache"
2319
else
2420
echo "DFX not restored from cache, running install script:"
25-
DFX_VERSION=${{ env.dfx_version }} sh -ci "$(curl -fsSL https://sdk.dfinity.org/install.sh)"
21+
DFX_VERSION=0.14.2 sh -ci "$(curl -fsSL https://sdk.dfinity.org/install.sh)"
2622
fi
2723
echo "DFX version"
2824
dfx --version

.github/workflows/pull-request.yml

+3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ jobs:
4848
- name: Build NPM packages
4949
run: pnpm build
5050

51+
- name: Test NPM packages
52+
run: pnpm test
53+
5154
- name: Test Cargo crates
5255
run: cargo test
5356

Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ members = [
1111
# these projects need wasm32-unknown-unknown as their target
1212
exclude = [
1313
"examples/certified-counter/src/backend",
14-
"packages/ic-response-verification-wasm"
14+
"packages/ic-response-verification-wasm",
15+
"packages/ic-certification-test-utils-wasm",
1516
]
1617

1718
[profile.release]

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,7 @@ See [Conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) for m
5757
| Project | Command | Description |
5858
| ----------------------------------- | ----------------------------------------------------------- | ------------------------------------------ |
5959
| All | `pnpm build` | Build all NPM projects |
60+
| `@dfinity/certificate-verification` | `pnpm run --filter @dfinity/certificate-verification build` | Build certificate verification |
61+
| `@dfinity/certificate-verification` | `pnpm run --filter @dfinity/certificate-verification test` | Test certificate verification |
6062
| `@dfinity/certification-test-utils` | `pnpm run --filter @dfinity/certification-test-utils build` | Build certification test utils |
6163
| `@dfinity/response-verification` | `pnpm run --filter @dfinity/response-verification build` | Build the response verification JS library |

examples/certified-counter/src/frontend/src/index.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ buttonElement.addEventListener('click', async event => {
3333

3434
const agent = new HttpAgent();
3535
await agent.fetchRootKey();
36-
const tree = await verifyCertification(
37-
Principal.fromText(canisterId),
38-
new Uint8Array(certificate).buffer,
39-
new Uint8Array(witness).buffer,
40-
agent.rootKey,
41-
50000,
42-
);
36+
const tree = await verifyCertification({
37+
canisterId: Principal.fromText(canisterId),
38+
encodedCertificate: new Uint8Array(certificate).buffer,
39+
encodedTree: new Uint8Array(witness).buffer,
40+
rootKey: agent.rootKey,
41+
maxCertificateTimeOffsetMs: 50000,
42+
});
4343

4444
const treeHash = lookup_path(['count'], tree);
4545
if (!treeHash) {

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"scripts": {
55
"build": "pnpm run -r build",
66
"format": "prettier --write .",
7-
"format:check": "prettier --check ."
7+
"format:check": "prettier --check .",
8+
"test": "pnpm run -r test"
89
},
910
"engines": {
1011
"node": "^18",

packages/certificate-verification-js/README.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ Check [ic-certified-map](https://github.com/dfinity/cdk-rs/tree/main/library/ic-
3333
```javascript
3434
const { data, certificate, witness } = await canister.get_data();
3535

36-
const tree = await verifyCertification(
37-
Principal.fromText(canisterId),
38-
new Uint8Array(certificate).buffer,
39-
new Uint8Array(witness).buffer,
40-
agent.rootKey,
41-
50000,
42-
);
36+
const tree = await verifyCertification({
37+
canisterId: Principal.fromText(canisterId),
38+
encodedCertificate: new Uint8Array(certificate).buffer,
39+
encodedTree: new Uint8Array(witness).buffer,
40+
rootKey: agent.rootKey,
41+
maxCertificateTimeOffsetMs: 50000,
42+
});
4343

4444
const treeDataHash = lookup_path(['count'], tree);
4545
const responseDataHash = calculateDataHash(data);

packages/certificate-verification-js/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,8 @@
3030
"@dfinity/agent": "~0.15.7",
3131
"@dfinity/candid": "~0.15.7",
3232
"@dfinity/principal": "~0.15.7"
33+
},
34+
"devDependencies": {
35+
"@dfinity/certification-test-utils": "workspace:*"
3336
}
3437
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { HashTree, reconstruct, Cbor } from '@dfinity/agent';
3+
import { CertificateBuilder } from '@dfinity/certification-test-utils';
4+
import { Principal } from '@dfinity/principal';
5+
import { createHash, webcrypto } from 'node:crypto';
6+
import { verifyCertification } from './index';
7+
8+
globalThis.crypto = webcrypto as Crypto;
9+
10+
describe('verifyCertification', () => {
11+
it('should verify a valid certificate with delegation', async () => {
12+
const userId = '1234';
13+
14+
const username = 'testuser';
15+
const usernameHash = new Uint8Array(
16+
createHash('sha256').update(username).digest(),
17+
);
18+
19+
const hashTree: HashTree = [
20+
2,
21+
new Uint8Array(Buffer.from(userId)),
22+
[3, usernameHash],
23+
];
24+
const rootHash = await reconstruct(hashTree);
25+
const cborEncodedTree = Cbor.encode(hashTree);
26+
27+
const canisterId = Principal.fromUint8Array(
28+
new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1]),
29+
);
30+
const time = BigInt(Date.now());
31+
32+
const certificate = new CertificateBuilder(
33+
canisterId.toString(),
34+
new Uint8Array(rootHash),
35+
)
36+
.withTime(time)
37+
.withDelegation(123n, [{ high: 10, low: 0 }])
38+
.build();
39+
40+
const decodedHashTree = await verifyCertification({
41+
canisterId,
42+
encodedCertificate: certificate.cborEncodedCertificate,
43+
encodedTree: cborEncodedTree,
44+
maxCertificateTimeOffsetMs: 5000,
45+
rootKey: certificate.rootKey,
46+
});
47+
expect(decodedHashTree).toEqual(hashTree);
48+
});
49+
});

packages/certificate-verification-js/src/index.ts

+15-7
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,21 @@ import { Principal } from '@dfinity/principal';
99
import { PipeArrayBuffer, lebDecode } from '@dfinity/candid';
1010
import { CertificateTimeError, CertificateVerificationError } from './error';
1111

12-
export async function verifyCertification(
13-
canisterId: Principal,
14-
encodedCertificate: ArrayBuffer,
15-
encodedTree: ArrayBuffer,
16-
rootKey: ArrayBuffer,
17-
maxCertificateTimeOffsetMs: number,
18-
): Promise<HashTree> {
12+
export interface CertificationParams {
13+
canisterId: Principal;
14+
encodedCertificate: ArrayBuffer;
15+
encodedTree: ArrayBuffer;
16+
rootKey: ArrayBuffer;
17+
maxCertificateTimeOffsetMs: number;
18+
}
19+
20+
export async function verifyCertification({
21+
canisterId,
22+
encodedCertificate,
23+
encodedTree,
24+
rootKey,
25+
maxCertificateTimeOffsetMs,
26+
}: CertificationParams): Promise<HashTree> {
1927
const certificate = await Certificate.create({
2028
certificate: encodedCertificate,
2129
canisterId,

0 commit comments

Comments
 (0)