UNPKG

@provablehq/sdk

Version:

A Software Development Kit (SDK) for Zero-Knowledge Transactions

375 lines (374 loc) 19.3 kB
import { TransportFunction } from "./utils/utils.js"; import { EncryptedRecord } from "./models/record-provider/encryptedRecord.js"; import { CryptoBoxPubKey } from "./models/cryptoBoxPubkey.js"; import { OwnedFilter } from "./models/record-scanner/ownedFilter.js"; import { OwnedRecord } from "./models/record-provider/ownedRecord.js"; import { RecordProvider } from "./record-provider.js"; import { Field, ViewKey } from "./wasm.js"; import { RecordsFilter } from "./models/record-scanner/recordsFilter.js"; import { RegisterResult } from "./models/record-scanner/registrationResult.js"; import { RevokeResult } from "./models/record-scanner/revokeResult.js"; import { TagsResult } from "./models/record-scanner/tagsResult.js"; import { SerialNumbersResult } from "./models/record-scanner/serialNumbersResult.js"; import { StatusResult } from "./models/record-scanner/statusResult.js"; import { OwnedRecordsResult } from "./models/record-scanner/ownedRecordsResult.js"; import { EncryptedRecordsResult } from "./models/record-scanner/encryptedRecordsResult.js"; import { Account } from "./account.js"; /** * JWT data for optional authentication with the record scanning service (e.g. Provable API). * * @property {string} jwt The JWT token string. * @property {number} expiration Expiration time as a Unix timestamp (e.g. in milliseconds). */ export interface RecordScannerJWTData { jwt: string; expiration: number; } /** * Configuration for the record scanner. * * @property {string} url Base URL of the record scanning service (network path is appended by the SDK). * @property {string | { header: string, value: string }} [apiKey] API key as a string or as a custom header name and value. * @property {string} [consumerId] Required for JWT refresh when using authenticated record scanner (e.g. Provable API). * @property {RecordScannerJWTData} [jwtData] Optional JWT for auth. If omitted and apiKey + consumerId are set, JWT is refreshed when needed. * @property {ViewKey[]} [viewKeys] Optional view keys to use for local scanning and decryption. * @property {Account} [account] Optional account to use for local scanning and decryption. * @property {boolean} [cacheViewKeysOnRegister] Cache view keys in memory for faster scanning upon register. * @property {boolean} [autoReRegister] If true, on 422 from /owned attempt one re-register via registerEncrypted (when a view key is in viewKeys or account) and retry once. Default false. * @property {boolean} [decryptEnabled] If true, enable decryption of owned records (e.g. for use with the decrypt method). This is REQUIRED for findCreditsRecord/findCreditsRecords to work properly. Further the ViewKey matching the UUID must be stored in the RecordScanner object to perform decryption. Default false. */ export interface RecordScannerOptions { url: string; apiKey?: string | { header: string; value: string; }; consumerId?: string; jwtData?: RecordScannerJWTData; viewKeys?: ViewKey[]; account?: Account; cacheViewKeysOnRegister?: boolean; autoReRegister?: boolean; decryptEnabled?: boolean; transport?: TransportFunction; } /** * RecordScanner is a RecordProvider implementation that uses Provable's confidential record scanning service to find * records. * * @example * const account = new Account({ privateKey: 'APrivateKey1...' }); * * const recordScanner = new RecordScanner({ url: "https://record-scanner.aleo.org" }); * recordScanner.setAccount(account); * recordScanner.setApiKey("example-api-key"); * const result = await recordScanner.registerEncrypted(viewKey, 0); * if (result.ok) { const uuid = result.data.uuid; } * * const filter = { * uuid, * filter: { * program: "credits.aleo", * records: ["credits"], * }, * responseFilter: { * commitment: true, * owner: true, * tag: true, * tag?: boolean; * sender: true, * spent: true, * record_ciphertext: true, * block_height: true; * block_timestamp: true; * output_index: true; * record_name: true; * function_name: true; * program_name: true; * transition_id: true; * transaction_id: true; * transaction_index: true; * transition_index: true; * }, * unspent: true, * }; * * const records = await recordScanner.findRecords(filter); */ declare class RecordScanner implements RecordProvider { readonly cacheViewKeysOnRegister?: boolean; readonly url: string; private readonly baseUrl; private apiKey?; private consumerId?; private jwtData?; private uuid?; private viewKeys?; private autoReRegister?; private decryptEnabled?; transport: TransportFunction; account?: Account | undefined; /** * @param {RecordScannerOptions} options Configuration for the record scanner. */ constructor(options: RecordScannerOptions); /** * Set the API key to use for the record scanner. * * @param {string | { header: string, value: string }} apiKey The API key to use for the record scanner. */ setApiKey(apiKey: string | { header: string; value: string; }): void; /** * Set the consumer ID used for JWT refresh when using authenticated record scanner (e.g. Provable API). * * @param {string} consumerId The consumer ID to use for JWT refresh. */ setConsumerId(consumerId: string): void; /** * Set JWT data for authentication. Optional; when not set, JWT can be refreshed from apiKey + consumerId if provided. * * @param {RecordScannerJWTData | undefined} jwtData The JWT data to use, or undefined to clear. */ setJwtData(jwtData: RecordScannerJWTData | undefined): void; /** * Set whether /owned should automatically re-register on 422 (when a view key for the UUID is in viewKeys or account) and retry once. * * @param {boolean} enabled Whether to enable auto re-register on 422. */ setAutoReRegister(enabled: boolean): void; /** * Set whether decryption of owned records is enabled (e.g. for use with the decrypt method). * * @param {boolean} enabled Whether to enable decryption of owned records received from the scanner using the `owned` or any `findRecords` methods. */ setDecryptEnabled(enabled: boolean): void; /** * Add a view key to the record scanner for usage in local decryption. This is REQUIRED for findCreditsRecord/findCreditsRecords to work properly. * * @param {ViewKey} viewKey The view key to add. */ addViewKey(viewKey: ViewKey): void; /** * Remove a view key from the record scanner. * * @param {string} uuid The uuid of the view key to remove. */ removeViewKey(uuid: string): void; /** * Return the view key for the given record-scanner UUID if one is configured * (in viewKeys or as the account's view key). Used to decide if re-registration on 422 is possible. * * @param {string} uuid The record-scanner UUID to look up. * @returns {ViewKey | undefined} The view key for that UUID, or undefined. */ private getViewKeyForUuid; /** * Set the primary account for the record scanner. * * @param {Account} account The account to set as the primary account. */ setAccount(account: Account): void; /** * Refreshes the JWT by making a POST request to /jwts/{consumer_id}. Used when authentication is required. * * @param {string} apiKey The API key to use for the refresh request. * @param {string} consumerId The consumer ID for the JWT endpoint. * @returns {Promise<RecordScannerJWTData>} The new JWT data. */ private refreshJwt; /** * Returns auth headers (e.g. Authorization with JWT). Refreshes JWT if expired and apiKey + consumerId are set. Empty when auth is not configured. * * @returns {Promise<Record<string, string>>} Auth headers to add to requests, or empty object when not configured. */ private getAuthHeaders; /** * Set the UUID for the record scanner. * * @param {Field | ViewKey} keyMaterial The UUID to use for the record scanner. If a ViewKey is provided, the UUID will be computed from the key. */ setUuid(keyMaterial: Field | ViewKey): void; /** * If the error is a RecordScannerRequestError (from request()), return a RecordScannerFailure result; * otherwise re-throw the error. * * @param {unknown} err The error from a failed request (e.g. from request() or from a catch after calling it). * @returns {RecordScannerFailure} When err is RecordScannerRequestError. * @throws Re-throws err when it is not a RecordScannerRequestError. */ private handleRequestError; /** * Fetches an ephemeral public key from the record scanning service for use with registerEncrypted. * Follows the same pattern as the delegated proving service /pubkey endpoint. * * @returns {Promise<CryptoBoxPubKey>} The service's ephemeral public key and key_id. */ getPubkey(): Promise<CryptoBoxPubKey>; /** * Registers the account with the record scanning service using the encrypted flow. * Alias of {@link registerEncrypted} — preserved so existing callers using the * previous unencrypted `register(viewKey, startBlock)` API continue to work * unchanged while transparently using the encrypted endpoint. * * @param {ViewKey} viewKey The view key to register. * @param {number} startBlock The block height to start scanning from. * @returns {Promise<RegisterResult>} `{ ok: true, data }` on success, or `{ ok: false, status, error }` on failure. */ register(viewKey: ViewKey, startBlock: number): Promise<RegisterResult>; /** * Registers the account with the record scanning service using the encrypted flow: 1. fetches an ephemeral public key from /pubkey - 2. encrypts the registration request (view key + start block) - 3. POSTs to /register/encrypted. Does not HTTP error on a proper error response from the record scanner; returns a result object instead. * * @param {ViewKey} viewKey The view key to register. * @param {number} startBlock The block height to start scanning from. * @returns {Promise<RegisterResult>} `{ ok: true, data }` on success, or `{ ok: false, status, error }` on failure. */ registerEncrypted(viewKey: ViewKey, startBlock: number): Promise<RegisterResult>; /** * Remove all local scanner state associated with the given UUID (stored uuid, viewKeys entry, account if it matches). * Called after a successful revoke so the scanner does not retain view keys or account for a revoked registration. */ private clearLocalStateForUuid; /** * Revoke the account registration with the record scanning service (POST /revoke). On success, also removes * all local state for that UUID: the stored UUID (if it matches), the view key for that UUID, and the * account (if its view key corresponds to that UUID). * * @param {string | Field | undefined} uuid The UUID to revoke. If omitted, uses the UUID configured on the scanner. * @returns {Promise<RevokeResult>} `{ ok: true, data: { status } }` on success, or `{ ok: false, status, error }` on failure. * @throws {UUIDError} When no UUID is configured and none provided, or when the UUID string is invalid. */ revoke(uuid?: string | Field): Promise<RevokeResult>; /** * Get encrypted records from the record scanning service. This is a safe variant of /records/encrypted that returns * a result instead of throwing on HTTP error. * * @param {RecordsFilter} recordsFilter The filter to use to find the records and filter the response. * @returns {Promise<EncryptedRecordsResult>} The encrypted records or an error if the request failed. */ encrypted(recordsFilter: RecordsFilter): Promise<EncryptedRecordsResult>; /** * Get encrypted records from the record scanning service. * * @param {RecordsFilter} recordsFilter The filter to use to find the records and filter the response. * @returns {Promise<EncryptedRecord[]>} The encrypted records. */ encryptedRecords(recordsFilter: RecordsFilter): Promise<EncryptedRecord[]>; /** * Check if serial numbers appear in any record inputs on-chain, indicating that the records they belong to have been spent. This is a safe variant of /records/sns that returns a result instead of throwing on HTTP error. * * @param {string[]} serialNumbers The serial numbers to check. * @returns {Promise<SerialNumbersResult>} Map of Aleo Record serial numbers and whether they appeared in any inputs on chain. If a boolean corresponding to the Serial Number has a true value, that Record is considered spent by the Aleo Network. */ serialNumbers(serialNumbers: string[]): Promise<SerialNumbersResult>; /** * Check if serial numbers appear in any record inputs on-chain, indicating that the records they belong to have been spent. * * @param {string[]} serialNumbers The serial numbers to check. * @returns {Promise<Record<string, boolean>>} Map of Aleo Record serial numbers and whether they appeared in any inputs on chain. If boolean corresponding to the Serial Number has a true value, that Record is considered spent by the Aleo Network. */ checkSerialNumbers(serialNumbers: string[]): Promise<Record<string, boolean>>; /** * Check if tags appear in any record inputs on-chain, indicating that the records they belong to have been spent. This is a safe variant of /records/tags that returns a result instead of throwing on HTTP error. * * * * @param {string[]} tags The tags to check. * @returns {Promise<TagsResult>} Map of Aleo Record tags and whether they appeared in any inputs on chain. If a boolean corresponding to the tag has a true value, that Record is considered spent by the Aleo Network. */ tags(tags: string[]): Promise<TagsResult>; /** * Check if tags appear in any record inputs on-chain, indicating that the records they belong to have been spent. * * @param {string[]} tags The tags to check. * @returns {Promise<Record<string, boolean>>} Map of Aleo Record tags and whether they appeared in any inputs on chain. If boolean corresponding to the tag has a true value, that Record is considered spent by the Aleo Network. */ checkTags(tags: string[]): Promise<Record<string, boolean>>; /** * Check the scan completion job status for a specific UUID. * * @param {string | Field | undefined} uuid The UUID of the job to check. If no UUID is provided as input, the UUID configured for the scanner will be used. * @returns {Promise<StatusResult>} The status of the job or an error if the job could not be found. */ status(uuid?: string | Field): Promise<StatusResult>; /** * Find a record in the record scanning service. * * @param {OwnedFilter} searchParameters The filter to use to find the record. * @returns {Promise<OwnedRecord>} The record. */ findRecord(searchParameters: OwnedFilter): Promise<OwnedRecord>; /** * Get owned records. Throws if the UUID passed in the OwnedFilter is invalid or is not configured in the record scanner otherwise returns the RESTFUL response from the record scanner. * * @param {OwnedFilter} filter The OwnedFilter used to specify the subset of owned records to select. * @returns {Promise<OwnedRecordsResult>} Record belonging to the uuid passed in the filter or set on the Record Scanner. */ owned(filter: OwnedFilter): Promise<OwnedRecordsResult>; /** * Find records using the record scanning service. * * @param {OwnedFilter} searchParameters The filter to use to find the records. * @returns {Promise<OwnedRecord[]>} The records. */ findRecords(searchParameters: OwnedFilter): Promise<OwnedRecord[]>; /** * Get RecordPlaintext from an OwnedRecord by parsing record_plaintext (trimmed). Returns null if missing or parse fails. Does not decrypt; decryption is handled only in owned(). */ private getPlaintext; /** * For each owned record provided, attempt to decrypt with the given view key. On success, sets record_plaintext on that record to the decrypted plaintext string. Records that fail to decrypt (e.g. wrong view key) or have no record_ciphertext are left unchanged. * * @param {ViewKey} viewKey The view key to use for decryption. * @param {OwnedRecord[]} records The owned records to decrypt (mutated in place). */ decrypt(viewKey: ViewKey, records: OwnedRecord[]): void; /** * Find a credits.aleo record in the record scanning service. * * @param {number} microcredits The amount of microcredits to find. * @param {OwnedFilter} searchParameters The filter to use to find the record. * @returns {Promise<OwnedRecord>} The record. */ findCreditsRecord(microcredits: number, searchParameters: OwnedFilter): Promise<OwnedRecord>; /** * Find credits records greater than or equal to the specified amounts using the record scanning service. * * @param {number[]} microcreditAmounts The amounts of microcredits to find. * @param {OwnedFilter} searchParameters The filter to use to find the records. * @returns {Promise<OwnedRecord[]>} The records */ findCreditsRecords(microcreditAmounts: number[], searchParameters: OwnedFilter): Promise<OwnedRecord[]>; /** * Wrapper function to make a request to the record scanning service and handle any errors. Optionally adds JWT Authorization header when consumerId/jwtData (or apiKey+consumerId) are configured. * * @param {Request} req The request to make. * @returns {Promise<Response>} The response when the request succeeds. * @throws {RecordScannerRequestError} When the server returns a non-2xx status (e.g. 4xx, 5xx). * @throws Re-throws any error from fetch (e.g. network failure) or from getAuthHeaders(). */ private request; /** * Compute the record scanner UUID for a view key. * * @param {ViewKey} viewKey The view key to compute the UUID for. * @returns {Field} The computed UUID corresponding to the view key. */ computeUUID(viewKey: ViewKey): Field; /** * Validate a UUID string to ensure it represents a valid Aleo Record Scanner UUID. * * @param {string} uuid The UUID to validate. * @returns {boolean} Whether the UUID is valid. */ uuidIsValid(uuid: string): boolean; /** * Get the uuid for the filter, first by extracting the UUID from the filter, then falling back to the uuid configured within the record scanner. * * @param {OwnedFilter} filter The filter to extract the UUID from. * @returns {string | undefined} The UUID for the filter, or undefined if the filter does not contain a UUID. */ private getUUID; } export { RecordScanner };