UNPKG

@provablehq/sdk

Version:

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

1,063 lines (1,060 loc) 51.9 kB
import 'core-js/proposals/json-parse-with-source.js'; import { ViewKey, ComputeKey, Address, PrivateKeyCiphertext, PrivateKey, RecordCiphertext, ProvingKey, VerifyingKey } from '@provablehq/wasm/mainnet.js'; export { Address, BHP1024, BHP256, BHP512, BHP768, Ciphertext, ComputeKey, ExecutionResponse, Field, Execution as FunctionExecution, Group, OfflineQuery, Pedersen128, Pedersen64, Plaintext, Poseidon2, Poseidon4, Poseidon8, PrivateKey, PrivateKeyCiphertext, Program, ProgramManager as ProgramManagerBase, ProvingKey, RecordCiphertext, RecordPlaintext, Scalar, Signature, Transaction, Transition, VerifyingKey, ViewKey, initThreadPool, verifyFunctionExecution } from '@provablehq/wasm/mainnet.js'; import { C as CREDITS_PROGRAM_KEYS, c as PRIVATE_TRANSFER, d as PRIVATE_TO_PUBLIC_TRANSFER, f as PUBLIC_TRANSFER, g as PUBLIC_TRANSFER_AS_SIGNER, h as PUBLIC_TO_PRIVATE_TRANSFER, l as logAndThrow } from './program-manager-B-18sj9m.js'; export { A as AleoKeyProvider, a as AleoKeyProviderParams, b as AleoNetworkClient, K as KEY_STORE, e as PRIVATE_TRANSFER_TYPES, P as ProgramManager, V as VALID_TRANSFER_TYPES } from './program-manager-B-18sj9m.js'; import { wrap } from 'comlink'; /** * Key Management class. Enables the creation of a new Aleo Account, importation of an existing account from * an existing private key or seed, and message signing and verification functionality. An Aleo Account is generated * from a randomly generated seed (number) from which an account private key, view key, and a public account address are * derived. The private key lies at the root of an Aleo account. It is a highly sensitive secret and should be protected * as it allows for creation of Aleo Program executions and arbitrary value transfers. The View Key allows for decryption * of a user's activity on the blockchain. The Address is the public address to which other users of Aleo can send Aleo * credits and other records to. This class should only be used in environments where the safety of the underlying key * material can be assured. * * @example * import { Account } from "@provablehq/sdk/testnet.js"; * * // Create a new account * const myRandomAccount = new Account(); * * // Create an account from a randomly generated seed * const seed = new Uint8Array([94, 91, 52, 251, 240, 230, 226, 35, 117, 253, 224, 210, 175, 13, 205, 120, 155, 214, 7, 169, 66, 62, 206, 50, 188, 40, 29, 122, 40, 250, 54, 18]); * const mySeededAccount = new Account({seed: seed}); * * // Create an account from an existing private key * const myExistingAccount = new Account({privateKey: process.env.privateKey}); * * // Sign a message * const hello_world = Uint8Array.from([104, 101, 108, 108, 111 119, 111, 114, 108, 100]); * const signature = myRandomAccount.sign(hello_world); * * // Verify a signature * assert(myRandomAccount.verify(hello_world, signature)); */ class Account { _privateKey; _viewKey; _computeKey; _address; constructor(params = {}) { try { this._privateKey = this.privateKeyFromParams(params); } catch (e) { console.error("Wrong parameter", e); throw new Error("Wrong Parameter"); } this._viewKey = ViewKey.from_private_key(this._privateKey); this._computeKey = ComputeKey.from_private_key(this._privateKey); this._address = Address.from_private_key(this._privateKey); } /** * Attempts to create an account from a private key ciphertext * @param {PrivateKeyCiphertext | string} ciphertext The encrypted private key ciphertext or its string representation * @param {string} password The password used to decrypt the private key ciphertext * @returns {Account} A new Account instance created from the decrypted private key * * @example * import { Account } from "@provablehq/sdk/testnet.js"; * * // Create an account object from a previously encrypted ciphertext and password. * const account = Account.fromCiphertext(process.env.ciphertext, process.env.password); */ static fromCiphertext(ciphertext, password) { try { ciphertext = (typeof ciphertext === "string") ? PrivateKeyCiphertext.fromString(ciphertext) : ciphertext; const _privateKey = PrivateKey.fromPrivateKeyCiphertext(ciphertext, password); return new Account({ privateKey: _privateKey.to_string() }); } catch (e) { throw new Error("Wrong password or invalid ciphertext"); } } /** * Creates a PrivateKey from the provided parameters. * @param {AccountParam} params The parameters containing either a private key string or a seed * @returns {PrivateKey} A PrivateKey instance derived from the provided parameters */ privateKeyFromParams(params) { if (params.seed) { return PrivateKey.from_seed_unchecked(params.seed); } if (params.privateKey) { return PrivateKey.from_string(params.privateKey); } return new PrivateKey(); } /** * Returns the PrivateKey associated with the account. * @returns {PrivateKey} The private key of the account * * @example * import { Account } from "@provablehq/sdk/testnet.js"; * * const account = new Account(); * const privateKey = account.privateKey(); */ privateKey() { return this._privateKey; } /** * Returns the ViewKey associated with the account. * @returns {ViewKey} The view key of the account * * @example * import { Account } from "@provablehq/sdk/testnet.js"; * * const account = new Account(); * const viewKey = account.viewKey(); */ viewKey() { return this._viewKey; } /** * Returns the ComputeKey associated with the account. * @returns {ComputeKey} The compute key of the account * * @example * import { Account } from "@provablehq/sdk/testnet.js"; * * const account = new Account(); * const computeKey = account.computeKey(); */ computeKey() { return this._computeKey; } /** * Returns the Aleo address associated with the account. * @returns {Address} The public address of the account * * @example * import { Account } from "@provablehq/sdk/testnet.js"; * * const account = new Account(); * const address = account.address(); */ address() { return this._address; } /** * Deep clones the Account. * @returns {Account} A new Account instance with the same private key * * @example * import { Account } from "@provablehq/sdk/testnet.js"; * * const account = new Account(); * const clonedAccount = account.clone(); */ clone() { return new Account({ privateKey: this._privateKey.to_string() }); } /** * Returns the address of the account in a string representation. * * @returns {string} The string representation of the account address */ toString() { return this.address().to_string(); } /** * Encrypts the account's private key with a password. * * @param {string} password Password to encrypt the private key. * @returns {PrivateKeyCiphertext} The encrypted private key ciphertext * * @example * import { Account } from "@provablehq/sdk/testnet.js"; * * const account = new Account(); * const ciphertext = account.encryptAccount("password"); * process.env.ciphertext = ciphertext.toString(); */ encryptAccount(password) { return this._privateKey.toCiphertext(password); } /** * Decrypts an encrypted record string into a plaintext record object. * * @param {string} ciphertext A string representing the ciphertext of a record. * @returns {RecordPlaintext} The decrypted record plaintext * * @example * // Import the AleoNetworkClient and Account classes * import { AleoNetworkClient, Account } from "@provablehq/sdk/testnet.js"; * * // Create a connection to the Aleo network and an account * const networkClient = new AleoNetworkClient("https://api.explorer.provable.com/v1"); * const account = Account.fromCiphertext(process.env.ciphertext!, process.env.password!); * * // Get the record ciphertexts from a transaction. * const transaction = await networkClient.getTransactionObject("at1fjy6s9md2v4rgcn3j3q4qndtfaa2zvg58a4uha0rujvrn4cumu9qfazxdd"); * const records = transaction.records(); * * // Decrypt any records the account owns. * const decryptedRecords = []; * for (const record of records) { * if (account.decryptRecord(record)) { * decryptedRecords.push(record); * } * } */ decryptRecord(ciphertext) { return this._viewKey.decrypt(ciphertext); } /** * Decrypts an array of Record ciphertext strings into an array of record plaintext objects. * * @param {string[]} ciphertexts An array of strings representing the ciphertexts of records. * @returns {RecordPlaintext[]} An array of decrypted record plaintexts * * @example * // Import the AleoNetworkClient and Account classes * import { AleoNetworkClient, Account } from "@provablehq/sdk/testnet.js"; * * // Create a connection to the Aleo network and an account * const networkClient = new AleoNetworkClient("https://api.explorer.provable.com/v1"); * const account = Account.fromCiphertext(process.env.ciphertext!, process.env.password!); * * // Get the record ciphertexts from a transaction. * const transaction = await networkClient.getTransactionObject("at1fjy6s9md2v4rgcn3j3q4qndtfaa2zvg58a4uha0rujvrn4cumu9qfazxdd"); * const records = transaction.records(); * * // Decrypt any records the account owns. If the account owns no records, the array will be empty. * const decryptedRecords = account.decryptRecords(records); */ decryptRecords(ciphertexts) { return ciphertexts.map((ciphertext) => this._viewKey.decrypt(ciphertext)); } /** * Determines whether the account owns a ciphertext record. * @param {RecordCiphertext | string} ciphertext The record ciphertext to check ownership of * @returns {boolean} True if the account owns the record, false otherwise * * @example * // Import the AleoNetworkClient and Account classes * import { AleoNetworkClient, Account } from "@provablehq/sdk/testnet.js"; * * // Create a connection to the Aleo network and an account * const networkClient = new AleoNetworkClient("https://api.explorer.provable.com/v1"); * const account = Account.fromCiphertext(process.env.ciphertext!, process.env.password!); * * // Get the record ciphertexts from a transaction and check ownership of them. * const transaction = await networkClient.getTransactionObject("at1fjy6s9md2v4rgcn3j3q4qndtfaa2zvg58a4uha0rujvrn4cumu9qfazxdd"); * const records = transaction.records(); * * // Check if the account owns any of the record ciphertexts present in the transaction. * const ownedRecords = []; * for (const record of records) { * if (account.ownsRecordCiphertext(record)) { * ownedRecords.push(record); * } * } */ ownsRecordCiphertext(ciphertext) { if (typeof ciphertext === 'string') { try { const ciphertextObject = RecordCiphertext.fromString(ciphertext); return ciphertextObject.isOwner(this._viewKey); } catch (e) { return false; } } else { return ciphertext.isOwner(this._viewKey); } } /** * Signs a message with the account's private key. * Returns a Signature. * * @param {Uint8Array} message Message to be signed. * @returns {Signature} Signature over the message in bytes. * * @example * // Import the Account class * import { Account } from "@provablehq/sdk/testnet.js"; * * // Create a connection to the Aleo network and an account * const account = Account.fromCiphertext(process.env.ciphertext, process.env.password); * * // Create an account and a message to sign. * const account = new Account(); * const message = Uint8Array.from([104, 101, 108, 108, 111 119, 111, 114, 108, 100]) * const signature = account.sign(message); * * // Verify the signature. * assert(account.verify(message, signature)); */ sign(message) { return this._privateKey.sign(message); } /** * Verifies the Signature on a message. * * @param {Uint8Array} message Message in bytes to be signed. * @param {Signature} signature Signature to be verified. * @returns {boolean} True if the signature is valid, false otherwise. * * @example * // Import the Account class * import { Account } from "@provablehq/sdk/testnet.js"; * * // Create a connection to the Aleo network and an account * const account = Account.fromCiphertext(process.env.ciphertext, process.env.password); * * // Sign a message. * const message = Uint8Array.from([104, 101, 108, 108, 111 119, 111, 114, 108, 100]) * const signature = account.sign(message); * * // Verify the signature. * assert(account.verify(message, signature)); */ verify(message, signature) { return this._address.verify(message, signature); } } /** * Search parameters for the offline key provider. This class implements the KeySearchParams interface and includes * a convenience method for creating a new instance of this class for each function of the credits.aleo program. * * @example * // If storing a key for a custom program function * offlineSearchParams = new OfflineSearchParams("myprogram.aleo/myfunction"); * * // If storing a key for a credits.aleo program function * bondPublicKeyParams = OfflineSearchParams.bondPublicKeyParams(); */ class OfflineSearchParams { cacheKey; verifyCreditsKeys; /** * Create a new OfflineSearchParams instance. * * @param {string} cacheKey - Key used to store the local function proving & verifying keys. This should be stored * under the naming convention "programName/functionName" (i.e. "myprogram.aleo/myfunction") * @param {boolean} verifyCreditsKeys - Whether to verify the keys against the credits.aleo program, * defaults to false, but should be set to true if using keys from the credits.aleo program */ constructor(cacheKey, verifyCreditsKeys = false) { this.cacheKey = cacheKey; this.verifyCreditsKeys = verifyCreditsKeys; } /** * Create a new OfflineSearchParams instance for the bond_public function of the credits.aleo program. */ static bondPublicKeyParams() { return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.bond_public.locator, true); } /** * Create a new OfflineSearchParams instance for the bond_validator function of the credits.aleo program. */ static bondValidatorKeyParams() { return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.bond_validator.locator, true); } /** * Create a new OfflineSearchParams instance for the claim_unbond_public function of the */ static claimUnbondPublicKeyParams() { return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.claim_unbond_public.locator, true); } /** * Create a new OfflineSearchParams instance for the fee_private function of the credits.aleo program. */ static feePrivateKeyParams() { return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.fee_private.locator, true); } /** * Create a new OfflineSearchParams instance for the fee_public function of the credits.aleo program. */ static feePublicKeyParams() { return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.fee_public.locator, true); } /** * Create a new OfflineSearchParams instance for the inclusion prover function. */ static inclusionKeyParams() { return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.inclusion.locator, true); } /** * Create a new OfflineSearchParams instance for the join function of the credits.aleo program. */ static joinKeyParams() { return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.join.locator, true); } /** * Create a new OfflineSearchParams instance for the set_validator_state function of the credits.aleo program. */ static setValidatorStateKeyParams() { return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.set_validator_state.locator, true); } /** * Create a new OfflineSearchParams instance for the split function of the credits.aleo program. */ static splitKeyParams() { return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.split.locator, true); } /** * Create a new OfflineSearchParams instance for the transfer_private function of the credits.aleo program. */ static transferPrivateKeyParams() { return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.transfer_private.locator, true); } /** * Create a new OfflineSearchParams instance for the transfer_private_to_public function of the credits.aleo program. */ static transferPrivateToPublicKeyParams() { return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.transfer_private_to_public.locator, true); } /** * Create a new OfflineSearchParams instance for the transfer_public function of the credits.aleo program. */ static transferPublicKeyParams() { return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.transfer_public.locator, true); } /** * Create a new OfflineSearchParams instance for the transfer_public_as_signer function of the credits.aleo program. */ static transferPublicAsSignerKeyParams() { return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.transfer_public_as_signer.locator, true); } /** * Create a new OfflineSearchParams instance for the transfer_public_to_private function of the credits.aleo program. */ static transferPublicToPrivateKeyParams() { return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.transfer_public_to_private.locator, true); } /** * Create a new OfflineSearchParams instance for the unbond_public function of the credits.aleo program. */ static unbondPublicKeyParams() { return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.unbond_public.locator, true); } } /** * A key provider meant for building transactions offline on devices such as hardware wallets. This key provider is not * able to contact the internet for key material and instead relies on the user to insert Aleo function proving & * verifying keys from local storage prior to usage. * * @example * // Create an offline program manager * const programManager = new ProgramManager(); * * // Create a temporary account for the execution of the program * const account = new Account(); * programManager.setAccount(account); * * // Create the proving keys from the key bytes on the offline machine * console.log("Creating proving keys from local key files"); * const program = "program hello_hello.aleo; function hello: input r0 as u32.public; input r1 as u32.private; add r0 r1 into r2; output r2 as u32.private;"; * const myFunctionProver = await getLocalKey("/path/to/my/function/hello_hello.prover"); * const myFunctionVerifier = await getLocalKey("/path/to/my/function/hello_hello.verifier"); * const feePublicProvingKeyBytes = await getLocalKey("/path/to/credits.aleo/feePublic.prover"); * * myFunctionProvingKey = ProvingKey.fromBytes(myFunctionProver); * myFunctionVerifyingKey = VerifyingKey.fromBytes(myFunctionVerifier); * const feePublicProvingKey = ProvingKey.fromBytes(feePublicKeyBytes); * * // Create an offline key provider * console.log("Creating offline key provider"); * const offlineKeyProvider = new OfflineKeyProvider(); * * // Cache the keys * // Cache the proving and verifying keys for the custom hello function * OfflineKeyProvider.cacheKeys("hello_hello.aleo/hello", myFunctionProvingKey, myFunctionVerifyingKey); * * // Cache the proving key for the fee_public function (the verifying key is automatically cached) * OfflineKeyProvider.insertFeePublicKey(feePublicProvingKey); * * // Create an offline query using the latest state root in order to create the inclusion proof * const offlineQuery = new OfflineQuery("latestStateRoot"); * * // Insert the key provider into the program manager * programManager.setKeyProvider(offlineKeyProvider); * * // Create the offline search params * const offlineSearchParams = new OfflineSearchParams("hello_hello.aleo/hello"); * * // Create the offline transaction * const offlineExecuteTx = <Transaction>await this.buildExecutionTransaction("hello_hello.aleo", "hello", 1, false, ["5u32", "5u32"], undefined, offlineSearchParams, undefined, undefined, undefined, undefined, offlineQuery, program); * * // Broadcast the transaction later on a machine with internet access * const networkClient = new AleoNetworkClient("https://api.explorer.provable.com/v1"); * const txId = await networkClient.broadcastTransaction(offlineExecuteTx); */ class OfflineKeyProvider { cache; constructor() { this.cache = new Map(); } /** * Get bond_public function keys from the credits.aleo program. The keys must be cached prior to calling this * method for it to work. * * @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the bond_public function */ bondPublicKeys() { return this.functionKeys(OfflineSearchParams.bondPublicKeyParams()); } ; /** * Get bond_validator function keys from the credits.aleo program. The keys must be cached prior to calling this * method for it to work. * * @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the bond_public function */ bondValidatorKeys() { return this.functionKeys(OfflineSearchParams.bondValidatorKeyParams()); } ; /** * Cache a set of keys. This will overwrite any existing keys with the same keyId. The user can check if a keyId * exists in the cache using the containsKeys method prior to calling this method if overwriting is not desired. * * @param {string} keyId access key for the cache * @param {FunctionKeyPair} keys keys to cache */ cacheKeys(keyId, keys) { const [provingKey, verifyingKey] = keys; this.cache.set(keyId, [provingKey.toBytes(), verifyingKey.toBytes()]); } ; /** * Get unbond_public function keys from the credits.aleo program. The keys must be cached prior to calling this * method for it to work. * * @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the unbond_public function */ claimUnbondPublicKeys() { return this.functionKeys(OfflineSearchParams.claimUnbondPublicKeyParams()); } ; /** * Get arbitrary function key from the offline key provider cache. * * @param {KeySearchParams | undefined} params - Optional search parameters for the key provider * @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the specified program * * @example * /// First cache the keys from local offline resources * const offlineKeyProvider = new OfflineKeyProvider(); * const myFunctionVerifyingKey = VerifyingKey.fromString("verifier..."); * const myFunctionProvingKeyBytes = await readBinaryFile('./resources/myfunction.prover'); * const myFunctionProvingKey = ProvingKey.fromBytes(myFunctionProvingKeyBytes); * * /// Cache the keys for future use with a memorable locator * offlineKeyProvider.cacheKeys("myprogram.aleo/myfunction", [myFunctionProvingKey, myFunctionVerifyingKey]); * * /// When they're needed, retrieve the keys from the cache * * /// First create a search parameter object with the same locator used to cache the keys * const keyParams = new OfflineSearchParams("myprogram.aleo/myfunction"); * * /// Then retrieve the keys * const [myFunctionProver, myFunctionVerifier] = await offlineKeyProvider.functionKeys(keyParams); */ functionKeys(params) { return new Promise((resolve, reject) => { if (params === undefined) { reject(new Error("No search parameters provided, cannot retrieve keys")); } else { const keyId = params.cacheKey; const verifyCreditsKeys = params.verifyCreditsKeys; if (this.cache.has(keyId)) { const [provingKeyBytes, verifyingKeyBytes] = this.cache.get(keyId); const provingKey = ProvingKey.fromBytes(provingKeyBytes); const verifyingKey = VerifyingKey.fromBytes(verifyingKeyBytes); if (verifyCreditsKeys) { const keysMatchExpected = this.verifyCreditsKeys(keyId, provingKey, verifyingKey); if (!keysMatchExpected) { reject(new Error(`Cached keys do not match expected keys for ${keyId}`)); } } resolve([provingKey, verifyingKey]); } else { reject(new Error("Keys not found in cache for " + keyId)); } } }); } ; /** * Determines if the keys for a given credits function match the expected keys. * * @returns {boolean} Whether the keys match the expected keys */ verifyCreditsKeys(locator, provingKey, verifyingKey) { switch (locator) { case CREDITS_PROGRAM_KEYS.bond_public.locator: return provingKey.isBondPublicProver() && verifyingKey.isBondPublicVerifier(); case CREDITS_PROGRAM_KEYS.claim_unbond_public.locator: return provingKey.isClaimUnbondPublicProver() && verifyingKey.isClaimUnbondPublicVerifier(); case CREDITS_PROGRAM_KEYS.fee_private.locator: return provingKey.isFeePrivateProver() && verifyingKey.isFeePrivateVerifier(); case CREDITS_PROGRAM_KEYS.fee_public.locator: return provingKey.isFeePublicProver() && verifyingKey.isFeePublicVerifier(); case CREDITS_PROGRAM_KEYS.inclusion.locator: return provingKey.isInclusionProver() && verifyingKey.isInclusionVerifier(); case CREDITS_PROGRAM_KEYS.join.locator: return provingKey.isJoinProver() && verifyingKey.isJoinVerifier(); case CREDITS_PROGRAM_KEYS.set_validator_state.locator: return provingKey.isSetValidatorStateProver() && verifyingKey.isSetValidatorStateVerifier(); case CREDITS_PROGRAM_KEYS.split.locator: return provingKey.isSplitProver() && verifyingKey.isSplitVerifier(); case CREDITS_PROGRAM_KEYS.transfer_private.locator: return provingKey.isTransferPrivateProver() && verifyingKey.isTransferPrivateVerifier(); case CREDITS_PROGRAM_KEYS.transfer_private_to_public.locator: return provingKey.isTransferPrivateToPublicProver() && verifyingKey.isTransferPrivateToPublicVerifier(); case CREDITS_PROGRAM_KEYS.transfer_public.locator: return provingKey.isTransferPublicProver() && verifyingKey.isTransferPublicVerifier(); case CREDITS_PROGRAM_KEYS.transfer_public_to_private.locator: return provingKey.isTransferPublicToPrivateProver() && verifyingKey.isTransferPublicToPrivateVerifier(); case CREDITS_PROGRAM_KEYS.unbond_public.locator: return provingKey.isUnbondPublicProver() && verifyingKey.isUnbondPublicVerifier(); default: return false; } } /** * Get fee_private function keys from the credits.aleo program. The keys must be cached prior to calling this * method for it to work. * * @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the join function */ feePrivateKeys() { return this.functionKeys(OfflineSearchParams.feePrivateKeyParams()); } ; /** * Get fee_public function keys from the credits.aleo program. The keys must be cached prior to calling this * method for it to work. * * @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the join function */ feePublicKeys() { return this.functionKeys(OfflineSearchParams.feePublicKeyParams()); } ; /** * Get join function keys from the credits.aleo program. The keys must be cached prior to calling this * method for it to work. * * @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the join function */ joinKeys() { return this.functionKeys(OfflineSearchParams.joinKeyParams()); } ; /** * Get split function keys from the credits.aleo program. The keys must be cached prior to calling this * method for it to work. * * @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the join function */ splitKeys() { return this.functionKeys(OfflineSearchParams.splitKeyParams()); } ; /** * Get keys for a variant of the transfer function from the credits.aleo program. * * * @param {string} visibility Visibility of the transfer function (private, public, privateToPublic, publicToPrivate) * @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the specified transfer function * * @example * // Create a new OfflineKeyProvider * const offlineKeyProvider = new OfflineKeyProvider(); * * // Cache the keys for future use with the official locator * const transferPublicProvingKeyBytes = await readBinaryFile('./resources/transfer_public.prover.a74565e'); * const transferPublicProvingKey = ProvingKey.fromBytes(transferPublicProvingKeyBytes); * * // Cache the transfer_public keys for future use with the OfflinKeyProvider's convenience method for * // transfer_public (the verifying key will be cached automatically) * offlineKeyProvider.insertTransferPublicKeys(transferPublicProvingKey); * * /// When they're needed, retrieve the keys from the cache * const [transferPublicProvingKey, transferPublicVerifyingKey] = await keyProvider.transferKeys("public"); */ transferKeys(visibility) { if (PRIVATE_TRANSFER.has(visibility)) { return this.functionKeys(OfflineSearchParams.transferPrivateKeyParams()); } else if (PRIVATE_TO_PUBLIC_TRANSFER.has(visibility)) { return this.functionKeys(OfflineSearchParams.transferPrivateToPublicKeyParams()); } else if (PUBLIC_TRANSFER.has(visibility)) { return this.functionKeys(OfflineSearchParams.transferPublicKeyParams()); } else if (PUBLIC_TRANSFER_AS_SIGNER.has(visibility)) { return this.functionKeys(OfflineSearchParams.transferPublicAsSignerKeyParams()); } else if (PUBLIC_TO_PRIVATE_TRANSFER.has(visibility)) { return this.functionKeys(OfflineSearchParams.transferPublicToPrivateKeyParams()); } else { throw new Error("Invalid visibility type"); } } ; /** * Get unbond_public function keys from the credits.aleo program * * @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the join function */ async unBondPublicKeys() { return this.functionKeys(OfflineSearchParams.unbondPublicKeyParams()); } ; /** * Insert the proving and verifying keys for the bond_public function into the cache. Only the proving key needs * to be inserted, the verifying key is automatically inserted by the SDK. This function will automatically check * that the keys match the expected checksum for bond_public before inserting them into the cache. * * @param provingKey */ insertBondPublicKeys(provingKey) { if (provingKey.isBondPublicProver()) { this.cache.set(CREDITS_PROGRAM_KEYS.bond_public.locator, [provingKey.toBytes(), VerifyingKey.bondPublicVerifier().toBytes()]); } else { throw new Error("Attempted to insert invalid proving keys for bond_public"); } } /** * Insert the proving and verifying keys for the claim_unbond_public function into the cache. Only the proving key needs * to be inserted, the verifying key is automatically inserted by the SDK. This function will automatically check * that the keys match the expected checksum for claim_unbond_public before inserting them into the cache. * * @param provingKey */ insertClaimUnbondPublicKeys(provingKey) { if (provingKey.isClaimUnbondPublicProver()) { this.cache.set(CREDITS_PROGRAM_KEYS.claim_unbond_public.locator, [provingKey.toBytes(), VerifyingKey.claimUnbondPublicVerifier().toBytes()]); } else { throw new Error("Attempted to insert invalid proving keys for claim_unbond_public"); } } /** * Insert the proving and verifying keys for the fee_private function into the cache. Only the proving key needs * to be inserted, the verifying key is automatically inserted by the SDK. This function will automatically check * that the keys match the expected checksum for fee_private before inserting them into the cache. * * @param provingKey */ insertFeePrivateKeys(provingKey) { if (provingKey.isFeePrivateProver()) { this.cache.set(CREDITS_PROGRAM_KEYS.fee_private.locator, [provingKey.toBytes(), VerifyingKey.feePrivateVerifier().toBytes()]); } else { throw new Error("Attempted to insert invalid proving keys for fee_private"); } } /** * Insert the proving and verifying keys for the fee_public function into the cache. Only the proving key needs * to be inserted, the verifying key is automatically inserted by the SDK. This function will automatically check * that the keys match the expected checksum for fee_public before inserting them into the cache. * * @param provingKey */ insertFeePublicKeys(provingKey) { if (provingKey.isFeePublicProver()) { this.cache.set(CREDITS_PROGRAM_KEYS.fee_public.locator, [provingKey.toBytes(), VerifyingKey.feePublicVerifier().toBytes()]); } else { throw new Error("Attempted to insert invalid proving keys for fee_public"); } } /** * Insert the proving and verifying keys for the join function into the cache. Only the proving key needs * to be inserted, the verifying key is automatically inserted by the SDK. This function will automatically check * that the keys match the expected checksum for join before inserting them into the cache. * * @param provingKey */ insertJoinKeys(provingKey) { if (provingKey.isJoinProver()) { this.cache.set(CREDITS_PROGRAM_KEYS.join.locator, [provingKey.toBytes(), VerifyingKey.joinVerifier().toBytes()]); } else { throw new Error("Attempted to insert invalid proving keys for join"); } } /** * Insert the proving and verifying keys for the set_validator_state function into the cache. Only the proving key needs * to be inserted, the verifying key is automatically inserted by the SDK. This function will automatically check * that the keys match the expected checksum for set_validator_state before inserting them into the cache. * * @param provingKey */ insertSetValidatorStateKeys(provingKey) { if (provingKey.isSetValidatorStateProver()) { this.cache.set(CREDITS_PROGRAM_KEYS.set_validator_state.locator, [provingKey.toBytes(), VerifyingKey.setValidatorStateVerifier().toBytes()]); } else { throw new Error("Attempted to insert invalid proving keys for set_validator_state"); } } /** * Insert the proving and verifying keys for the split function into the cache. Only the proving key needs * to be inserted, the verifying key is automatically inserted by the SDK. This function will automatically check * that the keys match the expected checksum for split before inserting them into the cache. * * @param provingKey */ insertSplitKeys(provingKey) { if (provingKey.isSplitProver()) { this.cache.set(CREDITS_PROGRAM_KEYS.split.locator, [provingKey.toBytes(), VerifyingKey.splitVerifier().toBytes()]); } else { throw new Error("Attempted to insert invalid proving keys for split"); } } /** * Insert the proving and verifying keys for the transfer_private function into the cache. Only the proving key needs * to be inserted, the verifying key is automatically inserted by the SDK. This function will automatically check * that the keys match the expected checksum for transfer_private before inserting them into the cache. * * @param provingKey */ insertTransferPrivateKeys(provingKey) { if (provingKey.isTransferPrivateProver()) { this.cache.set(CREDITS_PROGRAM_KEYS.transfer_private.locator, [provingKey.toBytes(), VerifyingKey.transferPrivateVerifier().toBytes()]); } else { throw new Error("Attempted to insert invalid proving keys for transfer_private"); } } /** * Insert the proving and verifying keys for the transfer_private_to_public function into the cache. Only the proving key needs * to be inserted, the verifying key is automatically inserted by the SDK. This function will automatically check * that the keys match the expected checksum for transfer_private_to_public before inserting them into the cache. * * @param provingKey */ insertTransferPrivateToPublicKeys(provingKey) { if (provingKey.isTransferPrivateToPublicProver()) { this.cache.set(CREDITS_PROGRAM_KEYS.transfer_private_to_public.locator, [provingKey.toBytes(), VerifyingKey.transferPrivateToPublicVerifier().toBytes()]); } else { throw new Error("Attempted to insert invalid proving keys for transfer_private_to_public"); } } /** * Insert the proving and verifying keys for the transfer_public function into the cache. Only the proving key needs * to be inserted, the verifying key is automatically inserted by the SDK. This function will automatically check * that the keys match the expected checksum for transfer_public before inserting them into the cache. * * @param provingKey */ insertTransferPublicKeys(provingKey) { if (provingKey.isTransferPublicProver()) { this.cache.set(CREDITS_PROGRAM_KEYS.transfer_public.locator, [provingKey.toBytes(), VerifyingKey.transferPublicVerifier().toBytes()]); } else { throw new Error("Attempted to insert invalid proving keys for transfer_public"); } } /** * Insert the proving and verifying keys for the transfer_public_to_private function into the cache. Only the proving key needs * to be inserted, the verifying key is automatically inserted by the SDK. This function will automatically check * that the keys match the expected checksum for transfer_public_to_private before inserting them into the cache. * * @param provingKey */ insertTransferPublicToPrivateKeys(provingKey) { if (provingKey.isTransferPublicToPrivateProver()) { this.cache.set(CREDITS_PROGRAM_KEYS.transfer_public_to_private.locator, [provingKey.toBytes(), VerifyingKey.transferPublicToPrivateVerifier().toBytes()]); } else { throw new Error("Attempted to insert invalid proving keys for transfer_public_to_private"); } } insertUnbondPublicKeys(provingKey) { if (provingKey.isUnbondPublicProver()) { this.cache.set(CREDITS_PROGRAM_KEYS.unbond_public.locator, [provingKey.toBytes(), VerifyingKey.unbondPublicVerifier().toBytes()]); } else { throw new Error("Attempted to insert invalid proving keys for unbond_public"); } } } /** * A record provider implementation that uses the official Aleo API to find records for usage in program execution and * deployment, wallet functionality, and other use cases. */ class NetworkRecordProvider { account; networkClient; constructor(account, networkClient) { this.account = account; this.networkClient = networkClient; } /** * Set the account used to search for records * * @param {Account} account The account to use for searching for records */ setAccount(account) { this.account = account; } /** * Find a list of credit records with a given number of microcredits by via the official Aleo API * * @param {number[]} microcredits The number of microcredits to search for * @param {boolean} unspent Whether or not the record is unspent * @param {string[]} nonces Nonces of records already found so that they are not found again * @param {RecordSearchParams} searchParameters Additional parameters to search for * @returns {Promise<RecordPlaintext>} The record if found, otherwise an error * * @example * // Create a new NetworkRecordProvider * const networkClient = new AleoNetworkClient("https://api.explorer.provable.com/v1"); * const keyProvider = new AleoKeyProvider(); * const recordProvider = new NetworkRecordProvider(account, networkClient); * * // The record provider can be used to find records with a given number of microcredits * const record = await recordProvider.findCreditsRecord(5000, true, []); * * // When a record is found but not yet used, it's nonce should be added to the nonces parameter so that it is not * // found again if a subsequent search is performed * const records = await recordProvider.findCreditsRecords(5000, true, [record.nonce()]); * * // When the program manager is initialized with the record provider it will be used to find automatically find * // fee records and amount records for value transfers so that they do not need to be specified manually * const programManager = new ProgramManager("https://api.explorer.provable.com/v1", keyProvider, recordProvider); * programManager.transfer(1, "aleo166q6ww6688cug7qxwe7nhctjpymydwzy2h7rscfmatqmfwnjvggqcad0at", "public", 0.5); * * */ async findCreditsRecords(microcredits, unspent, nonces, searchParameters) { let startHeight = 0; let endHeight = 0; let maxAmount = undefined; if (searchParameters) { if ("startHeight" in searchParameters && typeof searchParameters["startHeight"] == "number") { startHeight = searchParameters["startHeight"]; } if ("endHeight" in searchParameters && typeof searchParameters["endHeight"] == "number") { endHeight = searchParameters["endHeight"]; } if ("amounts" in searchParameters && Array.isArray(searchParameters["amounts"]) && searchParameters["amount"].every((item) => typeof item === 'number')) { microcredits = searchParameters["amounts"]; } if ("maxAmount" in searchParameters && typeof searchParameters["maxAmount"] == "number") { maxAmount = searchParameters["maxAmount"]; } if ("unspent" in searchParameters && typeof searchParameters["unspent"] == "boolean") { unspent = searchParameters["unspent"]; } } // If the end height is not specified, use the current block height if (endHeight == 0) { const end = await this.networkClient.getLatestHeight(); endHeight = end; } // If the start height is greater than the end height, throw an error if (startHeight >= endHeight) { logAndThrow("Start height must be less than end height"); } return await this.networkClient.findRecords(startHeight, endHeight, unspent, ["credits.aleo"], microcredits, maxAmount, nonces, this.account.privateKey()); } /** * Find a credit record with a given number of microcredits by via the official Aleo API * * @param {number} microcredits The number of microcredits to search for * @param {boolean} unspent Whether or not the record is unspent * @param {string[]} nonces Nonces of records already found so that they are not found again * @param {RecordSearchParams} searchParameters Additional parameters to search for * @returns {Promise<RecordPlaintext>} The record if found, otherwise an error * * @example * // Create a new NetworkRecordProvider * const networkClient = new AleoNetworkClient("https://api.explorer.provable.com/v1"); * const keyProvider = new AleoKeyProvider(); * const recordProvider = new NetworkRecordProvider(account, networkClient); * * // The record provider can be used to find records with a given number of microcredits * const record = await recordProvider.findCreditsRecord(5000, true, []); * * // When a record is found but not yet used, it's nonce should be added to the nonces parameter so that it is not * // found again if a subsequent search is performed * const records = await recordProvider.findCreditsRecords(5000, true, [record.nonce()]); * * // When the program manager is initialized with the record provider it will be used to find automatically find * // fee records and amount records for value transfers so that they do not need to be specified manually * const programManager = new ProgramManager("https://api.explorer.provable.com/v1", keyProvider, recordProvider); * programManager.transfer(1, "aleo166q6ww6688cug7qxwe7nhctjpymydwzy2h7rscfmatqmfwnjvggqcad0at", "public", 0.5); */ async findCreditsRecord(microcredits, unspent, nonces, searchParameters) { let records = null; try { records = await this.findCreditsRecords([microcredits], unspent, nonces, searchParameters); } catch (e) { console.log("No records found with error:", e); } if (records && records.length > 0) { return records[0]; } console.error("Record not found with error:", records); throw new Error("Record not found"); } /** * Find an arbitrary record. WARNING: This function is not implemented yet and will throw an error. */ async findRecord(unspent, nonces, searchParameters) { throw new Error("Not implemented"); } /** * Find multiple records from a specified program. */ async findRecords(unspent, nonces, searchParameters) { let startHeight = 0; let endHeight = 0; let amounts = undefined; let maxAmount = undefined; let programs = undefined; if (searchParameters) { if ("startHeight" in searchParameters && typeof searchParameters["startHeight"] == "number") { startHeight = searchParameters["startHeight"]; } if ("endHeight" in searchParameters && typeof searchParameters["endHeight"] == "number") { endHeight = searchParameters["endHeight"]; } if ("amounts" in searchParameters && Array.isArray(searchParameters["amounts"]) && searchParameters["amounts"].every((item) => typeof item === 'number')) { amounts = searchParameters["amounts"]; } if ("maxAmount" in searchParameters && typeof searchParameters["maxAmount"] == "number") { maxAmount = searchParameters["maxAmount"]; } if ("nonces" in searchParameters && Array.isArray(searchParameters["nonces"]) && searchParameters["nonces"].every((item) => typeof item === "string")) { nonces = searchParameters["nonces"]; } if ("program" in searchParameters && typeof searchParameters["program"] == "string") { programs = [searchParameters["program"]]; } if ("programs" in searchParameters && Array.isArray(searchParameters["programs"]) && searchParameters["programs"].every((item) => typeof item === "string")) { programs = searchParameters["programs"]; } if ("unspent" in searchParameters && typeof searchParameters["unspent"] == "boolean") { unspent = searchParameters["unspent"]; } } // If the end height is not specified, use the current block height if (endHeight == 0) { const end = await this.networkClient.getLatestHeight(); endHeight = end; } // If the start height is greater than the end height, throw an error if (startHeight >= endHeight) { logAndThrow("Start height must be less than end height"); } return await this.networkClient.findRecords(startHeight, endHeight, unspe