Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
41d9286
feat(agent): lookup canister ranges using the `/canister_ranges/<subn…
ilbertt Nov 26, 2025
09b0405
chore: update changelog
ilbertt Nov 26, 2025
160ef3d
Merge branch 'main' into luca/new-certificates-ranges
ilbertt Nov 26, 2025
73ad437
fix: index 0 is the first good shard
ilbertt Nov 27, 2025
c0b15f2
Merge remote-tracking branch 'origin/main' into luca/new-certificates…
ilbertt Dec 2, 2025
363fab7
Merge branch 'main' into luca/new-certificates-ranges
ilbertt Dec 2, 2025
2393a94
feat(agent)!: use `/api/v3` for query and read_state requests
ilbertt Dec 2, 2025
29e3a53
refactor: renames and changelog
ilbertt Dec 2, 2025
5703122
Merge branch 'main' into luca/new-certificates-ranges
ilbertt Dec 2, 2025
9d38401
Merge remote-tracking branch 'origin/luca/new-certificates-ranges' in…
ilbertt Dec 2, 2025
bb6daed
Merge branch 'main' into luca/api-v3-readstate
ilbertt Dec 2, 2025
8576b10
test: update snap
ilbertt Dec 2, 2025
a58680b
Merge remote-tracking branch 'origin/main' into luca/api-v3-readstate
ilbertt Dec 2, 2025
649cd0f
Merge remote-tracking branch 'origin/main' into luca/api-v3-readstate
ilbertt Dec 2, 2025
09c6c78
style: newline at the end
ilbertt Dec 2, 2025
bcfd07f
feat: subnet status module
ilbertt Dec 4, 2025
2dade09
chore: update changelog
ilbertt Dec 4, 2025
3f44bda
Merge branch 'main' into luca/subnet-status
ilbertt Dec 4, 2025
4e8b293
docs: add deprecation warning
ilbertt Dec 4, 2025
ce9c2a6
chore: update changelog
ilbertt Dec 4, 2025
f6f207e
Merge remote-tracking branch 'origin/luca/subnet-status' into luca/ap…
ilbertt Dec 4, 2025
95c67e5
docs: fix warning
ilbertt Dec 4, 2025
711999c
Merge remote-tracking branch 'origin/luca/subnet-status' into luca/ap…
ilbertt Dec 4, 2025
33021fa
fix: use subnet status to fetch node keys
ilbertt Dec 4, 2025
693958d
fix: do not verify canister ranges twice
ilbertt Dec 4, 2025
b79b0f5
test: update tests (wip)
ilbertt Dec 4, 2025
6c271a9
test: run all tests
ilbertt Dec 4, 2025
7d4c565
feat(agent): `syncTimeWithSubnet` method for `HttpAgent`
ilbertt Dec 4, 2025
f9f311c
Merge remote-tracking branch 'origin/main' into luca/sync-subnet-time
ilbertt Dec 4, 2025
31ea33d
Merge remote-tracking branch 'origin/luca/sync-subnet-time' into luca…
ilbertt Dec 4, 2025
9dd675d
Merge remote-tracking branch 'origin/main' into luca/api-v3-readstate
ilbertt Dec 9, 2025
5c436af
test: fix queryExpiry tests
ilbertt Dec 9, 2025
8f52c10
test: update e2e tests
ilbertt Dec 9, 2025
900ff4b
test: update syncTime tests
ilbertt Dec 9, 2025
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Renames `v3ResponseBody` to `v4ResponseBody`
- Renames `isV3ResponseBody` to `isV4ResponseBody`
- Renames `HttpV3ApiNotSupportedErrorCode` to `HttpV4ApiNotSupportedErrorCode`
- feat(agent)!: use `/api/v3` for query and read_state requests
- feat(agent)!: supports both subnet id and canister id for certificate verification
- The `canisterId` option has been replaced with the `principal` option in the `Certificate.create` options object
- feat(assets)!: replaces `@dfinity/{agent,candid,principal}` deps with `@icp-sdk/core`
Expand Down
118 changes: 59 additions & 59 deletions e2e/node/basic/__snapshots__/syncTime.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`syncTime > on async creation > should sync time on when enabled > V4 read state body one 1`] = `
exports[`syncTime > on async creation > should sync time when enabled > V3 read state body one 1`] = `
{
"content": {
"ingress_expiry": 1746103140000000000n,
Expand All @@ -23,7 +23,7 @@ exports[`syncTime > on async creation > should sync time on when enabled > V4 re
}
`;

exports[`syncTime > on async creation > should sync time on when enabled > V4 read state body three 1`] = `
exports[`syncTime > on async creation > should sync time when enabled > V3 read state body three 1`] = `
{
"content": {
"ingress_expiry": 1746103140000000000n,
Expand All @@ -46,7 +46,7 @@ exports[`syncTime > on async creation > should sync time on when enabled > V4 re
}
`;

exports[`syncTime > on async creation > should sync time on when enabled > V4 read state body two 1`] = `
exports[`syncTime > on async creation > should sync time when enabled > V3 read state body two 1`] = `
{
"content": {
"ingress_expiry": 1746103140000000000n,
Expand Down Expand Up @@ -116,7 +116,7 @@ exports[`syncTime > on error > should not sync time by default > V4 call body 1`
}
`;

exports[`syncTime > on error > should sync time when the local time does not match the subnet time > V2 read state body one 1`] = `
exports[`syncTime > on error > should sync time when the local time does not match the subnet time > V3 read state body one 1`] = `
{
"content": {
"ingress_expiry": 1746103140000000000n,
Expand All @@ -139,54 +139,7 @@ exports[`syncTime > on error > should sync time when the local time does not mat
}
`;

exports[`syncTime > on error > should sync time when the local time does not match the subnet time > V4 call body 1`] = `
{
"content": {
"arg": {
"data": [
68,
73,
68,
76,
0,
1,
113,
5,
119,
111,
114,
108,
100,
],
"type": "Buffer",
},
"canister_id": {
"data": [
255,
255,
255,
255,
255,
144,
0,
1,
1,
1,
],
"type": "Buffer",
},
"ingress_expiry": 1746103140000000000n,
"method_name": "greet",
"nonce": Any<Uint8Array>,
"request_type": "call",
"sender": Any<Uint8Array>,
},
"sender_pubkey": Any<Uint8Array>,
"sender_sig": Any<Uint8Array>,
}
`;

exports[`syncTime > on error > should sync time when the local time does not match the subnet time > V4 read state body three 1`] = `
exports[`syncTime > on error > should sync time when the local time does not match the subnet time > V3 read state body three 1`] = `
{
"content": {
"ingress_expiry": 1746103140000000000n,
Expand All @@ -209,7 +162,7 @@ exports[`syncTime > on error > should sync time when the local time does not mat
}
`;

exports[`syncTime > on error > should sync time when the local time does not match the subnet time > V4 read state body two 1`] = `
exports[`syncTime > on error > should sync time when the local time does not match the subnet time > V3 read state body two 1`] = `
{
"content": {
"ingress_expiry": 1746103140000000000n,
Expand All @@ -232,7 +185,7 @@ exports[`syncTime > on error > should sync time when the local time does not mat
}
`;

exports[`syncTime > on first call > should not sync time by default > V4 call body 1`] = `
exports[`syncTime > on error > should sync time when the local time does not match the subnet time > V4 call body 1`] = `
{
"content": {
"arg": {
Expand Down Expand Up @@ -279,7 +232,7 @@ exports[`syncTime > on first call > should not sync time by default > V4 call bo
}
`;

exports[`syncTime > on first call > should not sync time when explicitly disabled > V4 call body 1`] = `
exports[`syncTime > on first call > should not sync time by default > V4 call body 1`] = `
{
"content": {
"arg": {
Expand Down Expand Up @@ -326,7 +279,7 @@ exports[`syncTime > on first call > should not sync time when explicitly disable
}
`;

exports[`syncTime > on first call > should sync time when enabled > V4 call body 1`] = `
exports[`syncTime > on first call > should not sync time when explicitly disabled > V4 call body 1`] = `
{
"content": {
"arg": {
Expand Down Expand Up @@ -373,7 +326,7 @@ exports[`syncTime > on first call > should sync time when enabled > V4 call body
}
`;

exports[`syncTime > on first call > should sync time when enabled > V4 read state body one 1`] = `
exports[`syncTime > on first call > should sync time when enabled > V3 read state body one 1`] = `
{
"content": {
"ingress_expiry": 1746103140000000000n,
Expand All @@ -396,7 +349,7 @@ exports[`syncTime > on first call > should sync time when enabled > V4 read stat
}
`;

exports[`syncTime > on first call > should sync time when enabled > V4 read state body three 1`] = `
exports[`syncTime > on first call > should sync time when enabled > V3 read state body three 1`] = `
{
"content": {
"ingress_expiry": 1746103140000000000n,
Expand All @@ -419,7 +372,7 @@ exports[`syncTime > on first call > should sync time when enabled > V4 read stat
}
`;

exports[`syncTime > on first call > should sync time when enabled > V4 read state body two 1`] = `
exports[`syncTime > on first call > should sync time when enabled > V3 read state body two 1`] = `
{
"content": {
"ingress_expiry": 1746103140000000000n,
Expand All @@ -441,3 +394,50 @@ exports[`syncTime > on first call > should sync time when enabled > V4 read stat
},
}
`;

exports[`syncTime > on first call > should sync time when enabled > V4 call body 1`] = `
{
"content": {
"arg": {
"data": [
68,
73,
68,
76,
0,
1,
113,
5,
119,
111,
114,
108,
100,
],
"type": "Buffer",
},
"canister_id": {
"data": [
255,
255,
255,
255,
255,
144,
0,
1,
1,
1,
],
"type": "Buffer",
},
"ingress_expiry": 1746103140000000000n,
"method_name": "greet",
"nonce": Any<Uint8Array>,
"request_type": "call",
"sender": Any<Uint8Array>,
},
"sender_pubkey": Any<Uint8Array>,
"sender_sig": Any<Uint8Array>,
}
`;
20 changes: 10 additions & 10 deletions e2e/node/basic/canisterStatus.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { getCanisterId } from '../utils/canisterid.ts';
import {
MockReplica,
mockSyncTimeResponse,
prepareV2ReadStateSubnetResponse,
prepareV3ReadStateSubnetResponse,
} from '../utils/mock-replica.ts';
import { randomIdentity, randomKeyPair } from '../utils/identity.ts';

Expand Down Expand Up @@ -92,14 +92,14 @@ describe('canister status', () => {
identity,
});

const { responseBody: subnetResponseBody } = await prepareV2ReadStateSubnetResponse({
const { responseBody: subnetResponseBody } = await prepareV3ReadStateSubnetResponse({
nodeIdentity,
canisterRanges: [[canisterId.toUint8Array(), canisterId.toUint8Array()]],
keyPair: subnetKeyPair,
date: replicaDate,
});
// first try, fails
mockReplica.setV2ReadStateSpyImplOnce(canisterId.toString(), (_req, res) => {
mockReplica.setV3ReadStateSpyImplOnce(canisterId.toString(), (_req, res) => {
res.status(200).send(subnetResponseBody);
});
// syncs time
Expand All @@ -124,7 +124,7 @@ describe('canister status', () => {
expect(err.cause.code).toBeInstanceOf(CertificateTimeErrorCode);
expect(err.message).toContain('Certificate is signed more than 5 minutes in the past');
}
expect(mockReplica.getV2ReadStateSpy(canisterId.toString())).toHaveBeenCalledTimes(4);
expect(mockReplica.getV3ReadStateSpy(canisterId.toString())).toHaveBeenCalledTimes(4);
});

it('should sync time and succeed if the certificate is not fresh', async () => {
Expand All @@ -137,14 +137,14 @@ describe('canister status', () => {
identity,
});

const { responseBody: subnetResponseBody } = await prepareV2ReadStateSubnetResponse({
const { responseBody: subnetResponseBody } = await prepareV3ReadStateSubnetResponse({
nodeIdentity,
canisterRanges: [[canisterId.toUint8Array(), canisterId.toUint8Array()]],
keyPair: subnetKeyPair,
date: replicaDate,
});
// first try, fails
mockReplica.setV2ReadStateSpyImplOnce(canisterId.toString(), (_req, res) => {
mockReplica.setV3ReadStateSpyImplOnce(canisterId.toString(), (_req, res) => {
res.status(200).send(subnetResponseBody);
});
// sync time, we return the replica date to make the agent sync time properly
Expand All @@ -162,7 +162,7 @@ describe('canister status', () => {
paths: ['subnet'],
}),
).resolves.not.toThrow();
expect(mockReplica.getV2ReadStateSpy(canisterId.toString())).toHaveBeenCalledTimes(4);
expect(mockReplica.getV3ReadStateSpy(canisterId.toString())).toHaveBeenCalledTimes(4);
});

it('should not sync time and succeed if the certificate is not fresh and disableTimeVerification is true', async () => {
Expand All @@ -175,13 +175,13 @@ describe('canister status', () => {
identity,
});

const { responseBody: subnetResponseBody } = await prepareV2ReadStateSubnetResponse({
const { responseBody: subnetResponseBody } = await prepareV3ReadStateSubnetResponse({
nodeIdentity,
canisterRanges: [[canisterId.toUint8Array(), canisterId.toUint8Array()]],
keyPair: subnetKeyPair,
date: replicaDate,
});
mockReplica.setV2ReadStateSpyImplOnce(canisterId.toString(), (_req, res) => {
mockReplica.setV3ReadStateSpyImplOnce(canisterId.toString(), (_req, res) => {
res.status(200).send(subnetResponseBody);
});

Expand All @@ -193,7 +193,7 @@ describe('canister status', () => {
disableCertificateTimeVerification: true,
}),
).resolves.not.toThrow();
expect(mockReplica.getV2ReadStateSpy(canisterId.toString())).toHaveBeenCalledTimes(1);
expect(mockReplica.getV3ReadStateSpy(canisterId.toString())).toHaveBeenCalledTimes(1);
});
});
});
10 changes: 5 additions & 5 deletions e2e/node/basic/mainnet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {
CanisterStatus,
polling,
requestIdOf,
TrustError,
MissingSignatureErrorCode,
ProtocolError,
DerKeyLengthMismatchErrorCode,
} from '@icp-sdk/core/agent';
import { IDL } from '@icp-sdk/core/candid';
import { Ed25519KeyIdentity } from '@icp-sdk/core/identity';
Expand Down Expand Up @@ -206,9 +206,9 @@ test('it should allow you to set an incorrect root key', async () => {
try {
await actor.whoami();
} catch (error) {
expect(error).toBeInstanceOf(TrustError);
const errorCode = (error as TrustError).cause.code;
expect(errorCode).toBeInstanceOf(MissingSignatureErrorCode);
expect(error).toBeInstanceOf(ProtocolError);
const errorCode = (error as ProtocolError).cause.code;
expect(errorCode).toBeInstanceOf(DerKeyLengthMismatchErrorCode);
expect(errorCode.requestContext).toBeDefined();
}
});
Loading
Loading