Skip to content

Commit

Permalink
Merge pull request #907 from ProvableHQ/feat/key-provider-fix-example
Browse files Browse the repository at this point in the history
[Fix] Ensure credits.aleo keys are fetched from SnarkVM instead of a remote
  • Loading branch information
iamalwaysuncomfortable authored Aug 3, 2024
2 parents 6b53891 + 67ad3f1 commit 5dc5587
Show file tree
Hide file tree
Showing 13 changed files with 774 additions and 529 deletions.
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

0 comments on commit 5dc5587

Please sign in to comment.