@provablehq/sdk
Version:
A Software Development Kit (SDK) for Zero-Knowledge Transactions
1,063 lines (1,060 loc) • 51.9 kB
JavaScript
import 'core-js/proposals/json-parse-with-source.js';
import { ViewKey, ComputeKey, Address, PrivateKeyCiphertext, PrivateKey, RecordCiphertext, ProvingKey, VerifyingKey } from '@provablehq/wasm/testnet.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/testnet.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-BR5taTR7.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-BR5taTR7.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