Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] Ensure credits.aleo keys are fetched from SnarkVM instead of a remote #907

Merged
merged 9 commits into from
Aug 3, 2024
31 changes: 0 additions & 31 deletions index.js

This file was deleted.

95 changes: 78 additions & 17 deletions sdk/src/function-key-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
VerifyingKey,
CREDITS_PROGRAM_KEYS,
KEY_STORE,
Key,
PRIVATE_TRANSFER,
PRIVATE_TO_PUBLIC_TRANSFER,
PUBLIC_TRANSFER,
Expand Down Expand Up @@ -32,6 +33,7 @@ interface KeySearchParams {
* verifierUri to fetch keys via HTTP from a remote resource as well as a unique cacheKey to store the keys in memory.
*/
class AleoKeyProviderParams implements KeySearchParams {
name: string | undefined;
proverUri: string | undefined;
verifierUri: string | undefined;
cacheKey: string | undefined;
Expand All @@ -44,10 +46,11 @@ class AleoKeyProviderParams implements KeySearchParams {
*
* @param { AleoKeyProviderInitParams } params - Optional search parameters
*/
constructor(params: {proverUri?: string, verifierUri?: string, cacheKey?: string}) {
constructor(params: {proverUri?: string, verifierUri?: string, cacheKey?: string, name?: string}) {
this.proverUri = params.proverUri;
this.verifierUri = params.verifierUri;
this.cacheKey = params.cacheKey;
this.name = params.name;
}
}

Expand Down Expand Up @@ -328,6 +331,13 @@ class AleoKeyProvider implements FunctionKeyProvider {
let proverUrl;
let verifierUrl;
let cacheKey;
if ("name" in params && typeof params["name"] == "string") {
let key = CREDITS_PROGRAM_KEYS.getKey(params["name"]);
if (!(key instanceof Error)) {
return this.fetchCreditsKeys(key);
}
}

if ("proverUri" in params && typeof params["proverUri"] == "string") {
proverUrl = params["proverUri"];
}
Expand All @@ -341,7 +351,7 @@ class AleoKeyProvider implements FunctionKeyProvider {
}

if (proverUrl && verifierUrl) {
return await this.fetchKeys(proverUrl, verifierUrl, cacheKey);
return await this.fetchRemoteKeys(proverUrl, verifierUrl, cacheKey);
}

if (cacheKey) {
Expand Down Expand Up @@ -376,7 +386,7 @@ class AleoKeyProvider implements FunctionKeyProvider {
* CREDITS_PROGRAM_KEYS.transfer_private.verifier,
* );
*/
async fetchKeys(proverUrl: string, verifierUrl: string, cacheKey?: string): Promise<FunctionKeyPair | Error> {
async fetchRemoteKeys(proverUrl: string, verifierUrl: string, cacheKey?: string): Promise<FunctionKeyPair | Error> {
try {
// If cache is enabled, check if the keys have already been fetched and return them if they have
if (this.cacheOption) {
Expand Down Expand Up @@ -406,16 +416,67 @@ class AleoKeyProvider implements FunctionKeyProvider {
}
}

bondPublicKeys(): Promise<FunctionKeyPair | Error> {
return this.fetchKeys(CREDITS_PROGRAM_KEYS.bond_public.prover, CREDITS_PROGRAM_KEYS.bond_public.verifier, CREDITS_PROGRAM_KEYS.bond_public.locator)
/***
* Fetches the proving key from a remote source.
*
* @param proverUrl
* @param cacheKey
*
* @returns {Promise<ProvingKey | Error>} Proving key for the specified program
*/
async fetchProvingKey(proverUrl: string, cacheKey?: string): Promise<ProvingKey | Error> {
try {
// If cache is enabled, check if the keys have already been fetched and return them if they have
if (this.cacheOption) {
if (!cacheKey) {
cacheKey = proverUrl;
}
const value = this.cache.get(cacheKey);
if (typeof value !== "undefined") {
return ProvingKey.fromBytes(value[0]);
} else {
console.debug("Fetching proving keys from url " + proverUrl);
const provingKey = <ProvingKey>ProvingKey.fromBytes(await this.fetchBytes(proverUrl));
return provingKey;
}
}
else {
const provingKey = <ProvingKey>ProvingKey.fromBytes(await this.fetchBytes(proverUrl));
return provingKey;
}
} catch (error) {
throw new Error(`Error: ${error} fetching fee proving keys from ${proverUrl}`);
}
}

async fetchCreditsKeys(key: Key): Promise<FunctionKeyPair | Error> {
try {
if (!this.cache.has(key.locator) || !this.cacheOption) {
const verifying_key = key.verifyingKey()
const proving_key = <ProvingKey>await this.fetchProvingKey(key.prover, key.locator);
if (this.cacheOption) {
this.cache.set(CREDITS_PROGRAM_KEYS.bond_public.locator, [proving_key.toBytes(), verifying_key.toBytes()]);
}
return [proving_key, verifying_key];
} else {
const keyPair = <CachedKeyPair>this.cache.get(key.locator);
return [ProvingKey.fromBytes(keyPair[0]), VerifyingKey.fromBytes(keyPair[1])];
}
} catch (error) {
throw new Error(`Error: fetching credits.aleo keys: ${error}`);
}
}

async bondPublicKeys(): Promise<FunctionKeyPair | Error> {
return this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.bond_public);
}

bondValidatorKeys(): Promise<FunctionKeyPair | Error> {
return this.fetchKeys(CREDITS_PROGRAM_KEYS.bond_validator.prover, CREDITS_PROGRAM_KEYS.bond_validator.verifier, CREDITS_PROGRAM_KEYS.bond_validator.locator)
return this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.bond_validator);
}

claimUnbondPublicKeys(): Promise<FunctionKeyPair | Error> {
return this.fetchKeys(CREDITS_PROGRAM_KEYS.claim_unbond_public.prover, CREDITS_PROGRAM_KEYS.claim_unbond_public.verifier, CREDITS_PROGRAM_KEYS.claim_unbond_public.locator)
return this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.claim_unbond_public)
}

/**
Expand All @@ -438,15 +499,15 @@ class AleoKeyProvider implements FunctionKeyProvider {
*/
async transferKeys(visibility: string): Promise<FunctionKeyPair | Error> {
if (PRIVATE_TRANSFER.has(visibility)) {
return await this.fetchKeys(CREDITS_PROGRAM_KEYS.transfer_private.prover, CREDITS_PROGRAM_KEYS.transfer_private.verifier, CREDITS_PROGRAM_KEYS.transfer_private.locator);
return await this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.transfer_private);
} else if (PRIVATE_TO_PUBLIC_TRANSFER.has(visibility)) {
return await this.fetchKeys(CREDITS_PROGRAM_KEYS.transfer_private_to_public.prover, CREDITS_PROGRAM_KEYS.transfer_private_to_public.verifier, CREDITS_PROGRAM_KEYS.transfer_private_to_public.locator);
return await this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.transfer_private_to_public);
} else if (PUBLIC_TRANSFER.has(visibility)) {
return await this.fetchKeys(CREDITS_PROGRAM_KEYS.transfer_public.prover, CREDITS_PROGRAM_KEYS.transfer_public.verifier, CREDITS_PROGRAM_KEYS.transfer_public.locator);
return await this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.transfer_public);
} else if (PUBLIC_TRANSFER_AS_SIGNER.has(visibility)) {
return await this.fetchKeys(CREDITS_PROGRAM_KEYS.transfer_public_as_signer.prover, CREDITS_PROGRAM_KEYS.transfer_public_as_signer.verifier, CREDITS_PROGRAM_KEYS.transfer_public_as_signer.locator);
return await this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.transfer_public_as_signer);
} else if (PUBLIC_TO_PRIVATE_TRANSFER.has(visibility)) {
return await this.fetchKeys(CREDITS_PROGRAM_KEYS.transfer_public_to_private.prover, CREDITS_PROGRAM_KEYS.transfer_public_to_private.verifier, CREDITS_PROGRAM_KEYS.transfer_public_to_private.locator);
return await this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.transfer_public_to_private);
} else {
throw new Error("Invalid visibility type");
}
Expand All @@ -458,7 +519,7 @@ class AleoKeyProvider implements FunctionKeyProvider {
* @returns {Promise<FunctionKeyPair | Error>} Proving and verifying keys for the join function
*/
async joinKeys(): Promise<FunctionKeyPair | Error> {
return await this.fetchKeys(CREDITS_PROGRAM_KEYS.join.prover, CREDITS_PROGRAM_KEYS.join.verifier, CREDITS_PROGRAM_KEYS.join.locator);
return await this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.join);
}

/**
Expand All @@ -467,7 +528,7 @@ class AleoKeyProvider implements FunctionKeyProvider {
* @returns {Promise<FunctionKeyPair | Error>} Proving and verifying keys for the split function
* */
async splitKeys(): Promise<FunctionKeyPair | Error> {
return await this.fetchKeys(CREDITS_PROGRAM_KEYS.split.prover, CREDITS_PROGRAM_KEYS.split.verifier, CREDITS_PROGRAM_KEYS.split.locator);
return await this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.split);
}

/**
Expand All @@ -476,7 +537,7 @@ class AleoKeyProvider implements FunctionKeyProvider {
* @returns {Promise<FunctionKeyPair | Error>} Proving and verifying keys for the fee function
*/
async feePrivateKeys(): Promise<FunctionKeyPair | Error> {
return await this.fetchKeys(CREDITS_PROGRAM_KEYS.fee_private.prover, CREDITS_PROGRAM_KEYS.fee_private.verifier, CREDITS_PROGRAM_KEYS.fee_private.locator);
return await this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_private);
}

/**
Expand All @@ -485,7 +546,7 @@ class AleoKeyProvider implements FunctionKeyProvider {
* @returns {Promise<FunctionKeyPair | Error>} Proving and verifying keys for the fee function
*/
async feePublicKeys(): Promise<FunctionKeyPair | Error> {
return await this.fetchKeys(CREDITS_PROGRAM_KEYS.fee_public.prover, CREDITS_PROGRAM_KEYS.fee_public.verifier, CREDITS_PROGRAM_KEYS.fee_public.locator);
return await this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_public);
}

/**
Expand Down Expand Up @@ -544,7 +605,7 @@ class AleoKeyProvider implements FunctionKeyProvider {
}

unBondPublicKeys(): Promise<FunctionKeyPair | Error> {
return this.fetchKeys(CREDITS_PROGRAM_KEYS.unbond_public.prover, CREDITS_PROGRAM_KEYS.unbond_public.verifier, CREDITS_PROGRAM_KEYS.unbond_public.locator);
return this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.unbond_public);
}
}

Expand Down
10 changes: 10 additions & 0 deletions sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {VerifyingKey, Metadata} from "@provablehq/wasm";
const KEY_STORE = Metadata.baseUrl();

interface Key {
name: string,
locator: string,
prover: string,
verifier: string,
Expand All @@ -18,6 +19,7 @@ function convert(metadata: Metadata): Key {
}

return {
name: metadata.name,
locator: metadata.locator,
prover: metadata.prover,
verifier: metadata.verifier,
Expand All @@ -41,6 +43,13 @@ const CREDITS_PROGRAM_KEYS = {
transfer_public_as_signer: convert(Metadata.transfer_public_as_signer()),
transfer_public_to_private: convert(Metadata.transfer_public_to_private()),
unbond_public: convert(Metadata.unbond_public()),
getKey: function(key: string): Key | Error {
if (this.hasOwnProperty(key)) {
return (this as any)[key] as Key;
} else {
return new Error(`Key "${key}" not found.`);
}
}
};

const PRIVATE_TRANSFER_TYPES = new Set([
Expand Down Expand Up @@ -175,6 +184,7 @@ export {
FunctionKeyPair,
FunctionKeyProvider,
Input,
Key,
KeySearchParams,
NetworkRecordProvider,
ProgramImports,
Expand Down
14 changes: 4 additions & 10 deletions sdk/src/program-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,6 @@ class ProgramManager {
* @param {string} transferType The type of transfer to perform - options: 'private', 'privateToPublic', 'public', 'publicToPrivate'
* @param {number} fee The fee to pay for the transfer
* @param {boolean} privateFee Use a private record to pay the fee. If false this will use the account's public credit balance
* @param {string | undefined} caller The caller of the function (if calling transfer_public)
* @param {RecordSearchParams | undefined} recordSearchParams Optional parameters for finding the amount and fee
* records for the transfer transaction
* @param {RecordPlaintext | string} amountRecord Optional amount record to use for the transfer
Expand Down Expand Up @@ -625,7 +624,6 @@ class ProgramManager {
transferType: string,
fee: number,
privateFee: boolean,
caller?: string,
recordSearchParams?: RecordSearchParams,
amountRecord?: RecordPlaintext | string,
feeRecord?: RecordPlaintext | string,
Expand Down Expand Up @@ -674,14 +672,13 @@ class ProgramManager {
}

// Build an execution transaction and submit it to the network
return await WasmProgramManager.buildTransferTransaction(executionPrivateKey, amount, recipient, transferType, caller, amountRecord, fee, feeRecord, this.host, transferProvingKey, transferVerifyingKey, feeProvingKey, feeVerifyingKey, offlineQuery);
return await WasmProgramManager.buildTransferTransaction(executionPrivateKey, amount, recipient, transferType, amountRecord, fee, feeRecord, this.host, transferProvingKey, transferVerifyingKey, feeProvingKey, feeVerifyingKey, offlineQuery);
}

/**
* Build a transfer_public transaction to transfer credits to another account for later submission to the Aleo network
*
* @param {number} amount The amount of credits to transfer
* @param {string} caller The caller of the transfer (may be different from the signer)
* @param {string} recipient The recipient of the transfer
* @param {string} transferType The type of transfer to perform - options: 'private', 'privateToPublic', 'public', 'publicToPrivate'
* @param {number} fee The fee to pay for the transfer
Expand All @@ -696,13 +693,12 @@ class ProgramManager {
*/
async buildTransferPublicTransaction(
amount: number,
caller: string,
recipient: string,
fee: number,
privateKey?: PrivateKey,
offlineQuery?: OfflineQuery
): Promise<Transaction | Error> {
return this.buildTransferTransaction(amount, recipient, "public", fee, false, caller, undefined, undefined, undefined, privateKey, offlineQuery);
return this.buildTransferTransaction(amount, recipient, "public", fee, false, undefined, undefined, undefined, privateKey, offlineQuery);
}

/**
Expand All @@ -728,7 +724,7 @@ class ProgramManager {
privateKey?: PrivateKey,
offlineQuery?: OfflineQuery
): Promise<Transaction | Error> {
return this.buildTransferTransaction(amount, recipient, "public", fee, false, undefined, undefined, undefined, undefined, privateKey, offlineQuery);
return this.buildTransferTransaction(amount, recipient, "public", fee, false, undefined, undefined, undefined, privateKey, offlineQuery);
}

/**
Expand All @@ -739,7 +735,6 @@ class ProgramManager {
* @param {string} transferType The type of transfer to perform - options: 'private', 'privateToPublic', 'public', 'publicToPrivate'
* @param {number} fee The fee to pay for the transfer
* @param {boolean} privateFee Use a private record to pay the fee. If false this will use the account's public credit balance
* @param {string | undefined} caller The caller of the function (if calling transfer_public)
* @param {RecordSearchParams | undefined} recordSearchParams Optional parameters for finding the amount and fee
* records for the transfer transaction
* @param {RecordPlaintext | string} amountRecord Optional amount record to use for the transfer
Expand All @@ -766,14 +761,13 @@ class ProgramManager {
transferType: string,
fee: number,
privateFee: boolean,
caller?: string,
recordSearchParams?: RecordSearchParams,
amountRecord?: RecordPlaintext | string,
feeRecord?: RecordPlaintext | string,
privateKey?: PrivateKey,
offlineQuery?: OfflineQuery
): Promise<string | Error> {
const tx = <Transaction>await this.buildTransferTransaction(amount, recipient, transferType, fee, privateFee, caller, recordSearchParams, amountRecord, feeRecord, privateKey, offlineQuery);
const tx = <Transaction>await this.buildTransferTransaction(amount, recipient, transferType, fee, privateFee, recordSearchParams, amountRecord, feeRecord, privateKey, offlineQuery);
return await this.networkClient.submitTransaction(tx);
}

Expand Down
Loading
Loading