@dfinity/agent
Version:
JavaScript and TypeScript library to interact with the Internet Computer
301 lines • 14 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.encodePath = exports.fetchNodeKeys = exports.request = exports.CustomPath = void 0;
const principal_1 = require("@dfinity/principal");
const errors_ts_1 = require("../errors.js");
const certificate_ts_1 = require("../certificate.js");
const cbor = __importStar(require("../cbor.js"));
const leb_ts_1 = require("../utils/leb.js");
const utils_1 = require("@noble/hashes/utils");
/**
* Interface to define a custom path. Nested paths will be represented as individual buffers, and can be created from text using TextEncoder.
* @param {string} key the key to use to access the returned value in the canisterStatus map
* @param {Uint8Array[]} path the path to the desired value, represented as an array of buffers
* @param {string} decodeStrategy the strategy to use to decode the returned value
*/
class CustomPath {
constructor(key, path, decodeStrategy) {
this.key = key;
this.path = path;
this.decodeStrategy = decodeStrategy;
}
}
exports.CustomPath = CustomPath;
/**
* Requests information from a canister's `read_state` endpoint.
* Can be used to request information about the canister's controllers, time, module hash, candid interface, and more.
* @param {CanisterStatusOptions} options The configuration for the canister status request.
* @see {@link CanisterStatusOptions} for detailed options.
* @returns {Promise<StatusMap>} A map populated with data from the requested paths. Each path is a key in the map, and the value is the data obtained from the certificate for that path.
* @example
* const status = await canisterStatus({
* paths: ['controllers', 'candid'],
* ...options
* });
*
* const controllers = status.get('controllers');
*/
const request = async (options) => {
const { agent, paths, disableCertificateTimeVerification = false } = options;
const canisterId = principal_1.Principal.from(options.canisterId);
const uniquePaths = [...new Set(paths)];
const status = new Map();
const promises = uniquePaths.map((path, index) => {
const encodedPath = (0, exports.encodePath)(path, canisterId);
return (async () => {
try {
if (agent.rootKey === null) {
throw errors_ts_1.ExternalError.fromCode(new errors_ts_1.MissingRootKeyErrorCode());
}
const rootKey = agent.rootKey;
const response = await agent.readState(canisterId, {
paths: [encodedPath],
});
const certificate = await certificate_ts_1.Certificate.create({
certificate: response.certificate,
rootKey,
canisterId,
disableTimeVerification: disableCertificateTimeVerification,
agent,
});
const lookup = (cert, path) => {
if (path === 'subnet') {
const data = (0, exports.fetchNodeKeys)(response.certificate, canisterId, rootKey);
return {
path,
data,
};
}
else {
return {
path,
data: (0, certificate_ts_1.lookupResultToBuffer)(cert.lookup_path(encodedPath)),
};
}
};
// must pass in the rootKey if we have no delegation
const { path, data } = lookup(certificate, uniquePaths[index]);
if (!data) {
// Typically, the cert lookup will throw
console.warn(`Expected to find result for path ${path}, but instead found nothing.`);
if (typeof path === 'string') {
status.set(path, null);
}
else {
status.set(path.key, null);
}
}
else {
switch (path) {
case 'time': {
status.set(path, (0, leb_ts_1.decodeTime)(data));
break;
}
case 'controllers': {
status.set(path, decodeControllers(data));
break;
}
case 'module_hash': {
status.set(path, (0, utils_1.bytesToHex)(data));
break;
}
case 'subnet': {
status.set(path, data);
break;
}
case 'candid': {
status.set(path, new TextDecoder().decode(data));
break;
}
default: {
// Check for CustomPath signature
if (typeof path !== 'string' && 'key' in path && 'path' in path) {
switch (path.decodeStrategy) {
case 'raw':
status.set(path.key, data);
break;
case 'leb128': {
status.set(path.key, (0, leb_ts_1.decodeLeb128)(data));
break;
}
case 'cbor': {
status.set(path.key, cbor.decode(data));
break;
}
case 'hex': {
status.set(path.key, (0, utils_1.bytesToHex)(data));
break;
}
case 'utf-8': {
status.set(path.key, new TextDecoder().decode(data));
}
}
}
}
}
}
}
catch (error) {
// Throw on certificate errors
if (error instanceof errors_ts_1.AgentError &&
(error.hasCode(errors_ts_1.CertificateVerificationErrorCode) ||
error.hasCode(errors_ts_1.CertificateTimeErrorCode))) {
throw error;
}
if (typeof path !== 'string' && 'key' in path && 'path' in path) {
status.set(path.key, null);
}
else {
status.set(path, null);
}
console.group();
console.warn(`Expected to find result for path ${path}, but instead found nothing.`);
console.warn(error);
console.groupEnd();
}
})();
});
// Fetch all values separately, as each option can fail
await Promise.all(promises);
return status;
};
exports.request = request;
const fetchNodeKeys = (certificate, canisterId, root_key) => {
if (!canisterId._isPrincipal) {
throw errors_ts_1.InputError.fromCode(new errors_ts_1.UnexpectedErrorCode('Invalid canisterId'));
}
const cert = cbor.decode(certificate);
const tree = cert.tree;
let delegation = cert.delegation;
let subnetId;
if (delegation && delegation.subnet_id) {
subnetId = principal_1.Principal.fromUint8Array(new Uint8Array(delegation.subnet_id));
}
// On local replica, with System type subnet, there is no delegation
else if (!delegation && typeof root_key !== 'undefined') {
subnetId = principal_1.Principal.selfAuthenticating(new Uint8Array(root_key));
delegation = {
subnet_id: subnetId.toUint8Array(),
certificate: new Uint8Array(0),
};
}
// otherwise use default NNS subnet id
else {
subnetId = principal_1.Principal.selfAuthenticating(principal_1.Principal.fromText('tdb26-jop6k-aogll-7ltgs-eruif-6kk7m-qpktf-gdiqx-mxtrf-vb5e6-eqe').toUint8Array());
delegation = {
subnet_id: subnetId.toUint8Array(),
certificate: new Uint8Array(0),
};
}
const canisterInRange = (0, certificate_ts_1.check_canister_ranges)({ canisterId, subnetId, tree });
if (!canisterInRange) {
throw errors_ts_1.TrustError.fromCode(new errors_ts_1.CertificateNotAuthorizedErrorCode(canisterId, subnetId));
}
const subnetLookupResult = (0, certificate_ts_1.lookup_subtree)(['subnet', delegation.subnet_id, 'node'], tree);
if (subnetLookupResult.status !== certificate_ts_1.LookupSubtreeStatus.Found) {
throw errors_ts_1.ProtocolError.fromCode(new errors_ts_1.LookupErrorCode('Node not found', subnetLookupResult.status));
}
if (subnetLookupResult.value instanceof Uint8Array) {
throw errors_ts_1.UnknownError.fromCode(new errors_ts_1.HashTreeDecodeErrorCode('Invalid node tree'));
}
// The forks are all labeled trees with the <node_id> label
const nodeForks = (0, certificate_ts_1.flatten_forks)(subnetLookupResult.value);
const nodeKeys = new Map();
nodeForks.forEach(fork => {
const node_id = principal_1.Principal.from(fork[1]).toText();
const publicKeyLookupResult = (0, certificate_ts_1.lookup_path)(['public_key'], fork[2]);
if (publicKeyLookupResult.status !== certificate_ts_1.LookupPathStatus.Found) {
throw errors_ts_1.ProtocolError.fromCode(new errors_ts_1.LookupErrorCode('Public key not found', publicKeyLookupResult.status));
}
const derEncodedPublicKey = publicKeyLookupResult.value;
if (derEncodedPublicKey.byteLength !== 44) {
throw errors_ts_1.ProtocolError.fromCode(new errors_ts_1.DerKeyLengthMismatchErrorCode(44, derEncodedPublicKey.byteLength));
}
else {
nodeKeys.set(node_id, derEncodedPublicKey);
}
});
return {
subnetId: principal_1.Principal.fromUint8Array(new Uint8Array(delegation.subnet_id)).toText(),
nodeKeys,
};
};
exports.fetchNodeKeys = fetchNodeKeys;
const encodePath = (path, canisterId) => {
const canisterUint8Array = canisterId.toUint8Array();
switch (path) {
case 'time':
return [(0, utils_1.utf8ToBytes)('time')];
case 'controllers':
return [(0, utils_1.utf8ToBytes)('canister'), canisterUint8Array, (0, utils_1.utf8ToBytes)('controllers')];
case 'module_hash':
return [(0, utils_1.utf8ToBytes)('canister'), canisterUint8Array, (0, utils_1.utf8ToBytes)('module_hash')];
case 'subnet':
return [(0, utils_1.utf8ToBytes)('subnet')];
case 'candid':
return [
(0, utils_1.utf8ToBytes)('canister'),
canisterUint8Array,
(0, utils_1.utf8ToBytes)('metadata'),
(0, utils_1.utf8ToBytes)('candid:service'),
];
default: {
// Check for CustomPath signature
if ('key' in path && 'path' in path) {
// For simplified metadata queries
if (typeof path['path'] === 'string' || path['path'] instanceof Uint8Array) {
const metaPath = path.path;
const encoded = typeof metaPath === 'string' ? (0, utils_1.utf8ToBytes)(metaPath) : metaPath;
return [(0, utils_1.utf8ToBytes)('canister'), canisterUint8Array, (0, utils_1.utf8ToBytes)('metadata'), encoded];
// For non-metadata, return the provided custompath
}
else {
return path['path'];
}
}
}
}
throw errors_ts_1.UnknownError.fromCode(new errors_ts_1.UnexpectedErrorCode(`Error while encoding your path for canister status. Please ensure that your path ${path} was formatted correctly.`));
};
exports.encodePath = encodePath;
// Controllers are CBOR-encoded buffers
const decodeControllers = (buf) => {
const controllersRaw = cbor.decode(buf);
return controllersRaw.map(buf => {
return principal_1.Principal.fromUint8Array(buf);
});
};
//# sourceMappingURL=index.js.map
;