Skip to content

Commit 33021fa

Browse files
committed
fix: use subnet status to fetch node keys
1 parent 711999c commit 33021fa

File tree

1 file changed

+35
-25
lines changed
  • packages/core/src/agent/agent/http

1 file changed

+35
-25
lines changed

packages/core/src/agent/agent/http/index.ts

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ import {
5555
type ReadStateRequest,
5656
type HttpHeaderField,
5757
} from './types.ts';
58-
import { type SubnetStatus, request as canisterStatusRequest } from '../../canisterStatus/index.ts';
58+
import { request as canisterStatusRequest } from '../../canisterStatus/index.ts';
59+
import { request as subnetStatusRequest } from '../../subnetStatus/index.ts';
60+
import { type SubnetNodeKeys } from '../../utils/readState.ts';
5961
import { Certificate, type HashTree, lookup_path, LookupPathStatus } from '../../certificate.ts';
6062
import { ed25519 } from '@noble/curves/ed25519';
6163
import { ExpirableMap } from '../../utils/expirableMap.ts';
@@ -315,7 +317,7 @@ export class HttpAgent implements Agent {
315317
#queryPipeline: HttpAgentRequestTransformFn[] = [];
316318
#updatePipeline: HttpAgentRequestTransformFn[] = [];
317319

318-
#subnetKeys: ExpirableMap<string, SubnetStatus> = new ExpirableMap({
320+
#subnetKeys: ExpirableMap<string, SubnetNodeKeys> = new ExpirableMap({
319321
expirationTime: 5 * MINUTE_TO_MSECS,
320322
});
321323
#verifyQuerySignatures = true;
@@ -933,17 +935,17 @@ export class HttpAgent implements Agent {
933935
};
934936
};
935937

936-
const getSubnetStatus = async (): Promise<SubnetStatus> => {
937-
const cachedSubnetStatus = this.#subnetKeys.get(ecid.toString());
938-
if (cachedSubnetStatus) {
939-
return cachedSubnetStatus;
938+
const getSubnetNodeKeys = async (): Promise<SubnetNodeKeys> => {
939+
const cachedSubnetNodeKeys = this.#subnetKeys.get(ecid.toString());
940+
if (cachedSubnetNodeKeys) {
941+
return cachedSubnetNodeKeys;
940942
}
941943
await this.fetchSubnetKeys(ecid.toString());
942-
const subnetStatus = this.#subnetKeys.get(ecid.toString());
943-
if (!subnetStatus) {
944+
const subnetNodeKeys = this.#subnetKeys.get(ecid.toString());
945+
if (!subnetNodeKeys) {
944946
throw TrustError.fromCode(new MissingSignatureErrorCode());
945947
}
946-
return subnetStatus;
948+
return subnetNodeKeys;
947949
};
948950

949951
try {
@@ -953,16 +955,19 @@ export class HttpAgent implements Agent {
953955
}
954956

955957
// Make query and fetch subnet keys in parallel
956-
const [queryWithDetails, subnetStatus] = await Promise.all([makeQuery(), getSubnetStatus()]);
958+
const [queryWithDetails, subnetNodeKeys] = await Promise.all([
959+
makeQuery(),
960+
getSubnetNodeKeys(),
961+
]);
957962

958963
try {
959-
return this.#verifyQueryResponse(queryWithDetails, subnetStatus);
964+
return this.#verifyQueryResponse(queryWithDetails, subnetNodeKeys);
960965
} catch {
961966
// In case the node signatures have changed, refresh the subnet keys and try again
962967
this.log.warn('Query response verification failed. Retrying with fresh subnet keys.');
963968
this.#subnetKeys.delete(ecid.toString());
964-
const updatedSubnetStatus = await getSubnetStatus();
965-
return this.#verifyQueryResponse(queryWithDetails, updatedSubnetStatus);
969+
const updatedSubnetNodeKeys = await getSubnetNodeKeys();
970+
return this.#verifyQueryResponse(queryWithDetails, updatedSubnetNodeKeys);
966971
}
967972
} catch (error) {
968973
let queryError: AgentError;
@@ -986,12 +991,12 @@ export class HttpAgent implements Agent {
986991
/**
987992
* See https://internetcomputer.org/docs/current/references/ic-interface-spec/#http-query for details on validation
988993
* @param queryResponse - The response from the query
989-
* @param subnetStatus - The subnet status, including all node keys
994+
* @param subnetNodeKeys - The subnet node keys
990995
* @returns ApiQueryResponse
991996
*/
992997
#verifyQueryResponse = (
993998
queryResponse: ApiQueryResponse,
994-
subnetStatus: SubnetStatus,
999+
subnetNodeKeys: SubnetNodeKeys,
9951000
): ApiQueryResponse => {
9961001
if (this.#verifyQuerySignatures === false) {
9971002
// This should not be called if the user has disabled verification
@@ -1030,7 +1035,7 @@ export class HttpAgent implements Agent {
10301035
const separatorWithHash = concatBytes(IC_RESPONSE_DOMAIN_SEPARATOR, hash);
10311036

10321037
// FIX: check for match without verifying N times
1033-
const pubKey = subnetStatus.nodeKeys.get(nodeId);
1038+
const pubKey = subnetNodeKeys.get(nodeId);
10341039
if (!pubKey) {
10351040
throw ProtocolError.fromCode(new MalformedPublicKeyErrorCode());
10361041
}
@@ -1362,21 +1367,25 @@ export class HttpAgent implements Agent {
13621367
this.#identity = Promise.resolve(identity);
13631368
}
13641369

1365-
public async fetchSubnetKeys(canisterId: Principal | string) {
1370+
public async fetchSubnetKeys(
1371+
canisterId: Principal | string,
1372+
): Promise<SubnetNodeKeys | undefined> {
13661373
const effectiveCanisterId: Principal = Principal.from(canisterId);
13671374
await this.#asyncGuard(effectiveCanisterId);
1368-
const response = await canisterStatusRequest({
1369-
canisterId: effectiveCanisterId,
1370-
paths: ['subnet'],
1375+
1376+
const subnetId = await this.getSubnetIdFromCanister(effectiveCanisterId);
1377+
1378+
const response = await subnetStatusRequest({
1379+
subnetId,
1380+
paths: ['nodeKeys'],
13711381
agent: this,
13721382
});
13731383

1374-
const subnetResponse = response.get('subnet');
1375-
if (subnetResponse && typeof subnetResponse === 'object' && 'nodeKeys' in subnetResponse) {
1376-
this.#subnetKeys.set(effectiveCanisterId.toText(), subnetResponse as SubnetStatus);
1377-
return subnetResponse as SubnetStatus;
1384+
const nodeKeys = response.get('nodeKeys') as SubnetNodeKeys | undefined;
1385+
if (nodeKeys) {
1386+
this.#subnetKeys.set(effectiveCanisterId.toText(), nodeKeys);
1387+
return nodeKeys;
13781388
}
1379-
// If the subnet status is not returned, return undefined
13801389
return undefined;
13811390
}
13821391

@@ -1398,6 +1407,7 @@ export class HttpAgent implements Agent {
13981407
rootKey: this.rootKey!,
13991408
principal: { canisterId: effectiveCanisterId },
14001409
agent: this,
1410+
disableTimeVerification: true, // avoid extra calls to syncTime
14011411
});
14021412

14031413
if (!canisterCertificate.cert.delegation) {

0 commit comments

Comments
 (0)