Skip to content

Commit e136b9c

Browse files
Build proving request abstraction and supporting methods for generating proving requests
1 parent e17086b commit e136b9c

File tree

15 files changed

+804
-12
lines changed

15 files changed

+804
-12
lines changed

sdk/src/browser.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import { PlaintextArray} from "./models/plaintext/array.js";
2020
import { PlaintextLiteral} from "./models/plaintext/literal.js";
2121
import { PlaintextObject } from "./models/plaintext/plaintext.js";
2222
import { PlaintextStruct} from "./models/plaintext/struct.js";
23+
import { ProvingRequestJSON } from "./models/provingRequest.js";
24+
import { ProvingResponse } from "./models/provingResponse.js";
2325
import { RatificationJSON } from "./models/ratification.js";
2426
import { SolutionsJSON, SolutionJSON, PartialSolutionJSON } from "./models/solution.js";
2527
import { TransactionJSON } from "./models/transaction/transactionJSON.js";
@@ -53,7 +55,7 @@ async function initializeWasm() {
5355

5456
export { createAleoWorker } from "./managed-worker.js";
5557

56-
export { ProgramManager } from "./program-manager.js";
58+
export { ProgramManager, ProvingRequestOptions, ExecuteOptions, FeeAuthorizationOptions, AuthorizationOptions } from "./program-manager.js";
5759

5860
export { logAndThrow } from "./utils.js";
5961

@@ -84,6 +86,7 @@ export {
8486
Program,
8587
ProgramManager as ProgramManagerBase,
8688
ProvingKey,
89+
ProvingRequest,
8790
RecordCiphertext,
8891
RecordPlaintext,
8992
Signature,
@@ -150,6 +153,8 @@ export {
150153
PlaintextObject,
151154
PlaintextStruct,
152155
ProgramImports,
156+
ProvingRequestJSON,
157+
ProvingResponse,
153158
RatificationJSON,
154159
RecordProvider,
155160
RecordSearchParams,

sdk/src/models/provingRequest.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { AuthorizationJSON } from "./authorization";
2+
3+
export interface ProvingRequestJSON {
4+
authorization: AuthorizationJSON;
5+
fee_authorization?: AuthorizationJSON;
6+
broadcast: boolean;
7+
}

sdk/src/models/provingResponse.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { TransactionJSON } from "./transaction/transactionJSON";
2+
3+
export interface ProvingResponse {
4+
transaction: TransactionJSON,
5+
broadcast?: boolean,
6+
}

sdk/src/program-manager.ts

Lines changed: 182 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
VerifyingKey,
2525
Transaction,
2626
ProgramManager as WasmProgramManager,
27+
ProvingRequest,
2728
verifyFunctionExecution,
2829
} from "./wasm.js";
2930

@@ -71,6 +72,39 @@ interface ExecuteOptions {
7172
imports?: ProgramImports;
7273
}
7374

75+
/**
76+
* Represents the options for executing a transaction in the Aleo network.
77+
* This interface is used to specify the parameters required for building and submitting an execution transaction.
78+
*
79+
* @property {string} programName - The name of the program containing the function to be executed.
80+
* @property {string} functionName - The name of the function to execute within the program.
81+
* @property {number} baseFee - The base fee to be paid for the transaction.
82+
* @property {number} priorityFee - The optional priority fee to be paid for the transaction.
83+
* @property {boolean} privateFee - If true, uses a private record to pay the fee; otherwise, uses the account's public credit balance.
84+
* @property {string[]} inputs - The inputs to the function being executed.
85+
* @property {RecordSearchParams} [recordSearchParams] - Optional parameters for searching for a record to pay the execution transaction fee.
86+
* @property {string | RecordPlaintext} [feeRecord] - Optional fee record to use for the transaction.
87+
* @property {PrivateKey} [privateKey] - Optional private key to use for the transaction.
88+
* @property {string | Program} [program] - Optional program source code to use for the transaction.
89+
* @property {string} uri - The URI send the ProvingRequest to.
90+
* @property {ProgramImports} [imports] - Optional programs that the program being executed imports.
91+
* @property {boolean} broadcast - Whether to broadcast the Transaction generated by the remove prover to the Aleo network.
92+
*/
93+
interface ProvingRequestOptions {
94+
programName: string;
95+
functionName: string;
96+
baseFee: number,
97+
priorityFee: number;
98+
privateFee: boolean;
99+
inputs: string[];
100+
recordSearchParams?: RecordSearchParams;
101+
feeRecord?: string | RecordPlaintext;
102+
privateKey?: PrivateKey;
103+
programSource?: string | Program;
104+
programImports?: ProgramImports;
105+
broadcast?: boolean;
106+
}
107+
74108
/**
75109
* Options for building an Authorization for a function.
76110
*
@@ -696,6 +730,153 @@ class ProgramManager {
696730
);
697731
}
698732

733+
/**
734+
* Builds a `ProvingRequest` for submission to a prover for execution.
735+
*
736+
* @param {ProvingRequestOptions} options - The options for building the proving request
737+
* @returns {Promise<ProvingRequest>} - A promise that resolves to the transaction or an error.
738+
*
739+
* @example
740+
* /// Import the mainnet version of the sdk.
741+
* import { AleoKeyProvider, ProgramManager, NetworkRecordProvider } from "@provablehq/sdk/mainnet.js";
742+
*
743+
* // Create a new NetworkClient, KeyProvider, and RecordProvider.
744+
* const keyProvider = new AleoKeyProvider();
745+
* const recordProvider = new NetworkRecordProvider(account, networkClient);
746+
* keyProvider.useCache = true;
747+
*
748+
* // Initialize a ProgramManager with the key and record providers.
749+
* const programManager = new ProgramManager("https://api.explorer.provable.com/v1", keyProvider, recordProvider);
750+
*
751+
* // Build the proving request.
752+
* const provingRequest = await programManager.provingRequest({
753+
* programName: "credits.aleo",
754+
* functionName: "transfer_public",
755+
* baseFee: 100000,
756+
* priorityFee: 0,
757+
* privateFee: false,
758+
* inputs: [
759+
* "aleo1vwls2ete8dk8uu2kmkmzumd7q38fvshrht8hlc0a5362uq8ftgyqnm3w08",
760+
* "10000000u64",
761+
* ],
762+
* broadcast: true,
763+
* });
764+
*
765+
* // Submit the ProvingRequest to the network and await the response.
766+
* const provingResponse = await programManager.networkClient.submitProvingRequest(provingRequest);
767+
* // Get the transaction from the proving response.
768+
* const tx = provingResponse.transaction;
769+
* // Check if the proving service has already submitted the transaction.
770+
* const submitted = provingResponse.broadcast;
771+
*
772+
* // Get the transaction id.
773+
* const tx_id = tx.id();
774+
*
775+
* // If the response doesn't indicate the transaction was submitted, submit it manually.
776+
* if (!submitted) {
777+
* await programManager.networkClient.submitTransaction(tx);
778+
* }
779+
*
780+
* // Wait to see if the transaction has appeared on chain.
781+
* setTimeout(async () => {
782+
* const transaction = await programManager.networkClient.getTransaction(tx.id());
783+
* assert(transaction.id() === tx.id());
784+
* }, 10000);
785+
*/
786+
async provingRequest(
787+
options: ProvingRequestOptions,
788+
): Promise<ProvingRequest> {
789+
// Destructure the options object to access the parameters.
790+
const {
791+
programName,
792+
functionName,
793+
baseFee,
794+
priorityFee,
795+
privateFee,
796+
inputs,
797+
recordSearchParams,
798+
broadcast = false,
799+
} = options;
800+
801+
const privateKey = options.privateKey;
802+
let program = options.programSource;
803+
let feeRecord = options.feeRecord;
804+
let imports = options.programImports;
805+
806+
// Ensure the function exists on the network.
807+
if (program === undefined) {
808+
try {
809+
program = <string>(
810+
await this.networkClient.getProgram(programName)
811+
);
812+
} catch (e: any) {
813+
logAndThrow(
814+
`Error finding ${programName}. Network response: '${e.message}'. Please ensure you're connected to a valid Aleo network the program is deployed to the network.`,
815+
);
816+
}
817+
} else if (program instanceof Program) {
818+
program = program.toString();
819+
}
820+
821+
// Get the private key from the account if it is not provided in the parameters.
822+
let executionPrivateKey = privateKey;
823+
if (
824+
typeof privateKey === "undefined" &&
825+
typeof this.account !== "undefined"
826+
) {
827+
executionPrivateKey = this.account.privateKey();
828+
}
829+
830+
if (typeof executionPrivateKey === "undefined") {
831+
throw "No private key provided and no private key set in the ProgramManager";
832+
}
833+
834+
// Get the fee record from the account if it is not provided in the parameters.
835+
try {
836+
feeRecord = privateFee
837+
? <RecordPlaintext>(
838+
await this.getCreditsRecord(
839+
priorityFee,
840+
[],
841+
feeRecord,
842+
recordSearchParams,
843+
)
844+
)
845+
: undefined;
846+
} catch (e: any) {
847+
logAndThrow(
848+
`Error finding fee record. Record finder response: '${e.message}'. Please ensure you're connected to a valid Aleo network and a record with enough balance exists.`,
849+
);
850+
}
851+
852+
// Resolve the program imports if they exist.
853+
const numberOfImports = Program.fromString(program).getImports().length;
854+
if (numberOfImports > 0 && !imports) {
855+
try {
856+
imports = <ProgramImports>(
857+
await this.networkClient.getProgramImports(programName)
858+
);
859+
} catch (e: any) {
860+
logAndThrow(
861+
`Error finding program imports. Network response: '${e.message}'. Please ensure you're connected to a valid Aleo network and the program is deployed to the network.`,
862+
);
863+
}
864+
}
865+
866+
// Build and return the `ProvingRequest`.
867+
return await WasmProgramManager.buildProvingRequest(
868+
executionPrivateKey,
869+
program,
870+
functionName,
871+
inputs,
872+
baseFee,
873+
priorityFee,
874+
feeRecord,
875+
imports,
876+
broadcast
877+
);
878+
}
879+
699880
/**
700881
* Builds a SnarkVM fee `Authorization` for `credits.aleo/fee_private` or `credits.aleo/fee_public`. If a record is provided `fee_private` will be executed, otherwise `fee_public` will be executed.
701882
*
@@ -2283,4 +2464,4 @@ function validateTransferType(transferType: string): string {
22832464
);
22842465
}
22852466

2286-
export { ProgramManager, AuthorizationOptions, FeeAuthorizationOptions, ExecuteOptions };
2467+
export { ProgramManager, AuthorizationOptions, FeeAuthorizationOptions, ExecuteOptions, ProvingRequestOptions };

sdk/src/wasm.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export {
2626
Program,
2727
ProgramManager,
2828
ProvingKey,
29+
ProvingRequest,
2930
RecordCiphertext,
3031
RecordPlaintext,
3132
Scalar,

sdk/tests/network-client.test.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -434,15 +434,28 @@ describe("NodeConnection", () => {
434434
const mappings =
435435
await connection.getProgramMappingNames("credits.aleo");
436436
if (!(mappings instanceof Error)) {
437-
expect(mappings).deep.equal([
438-
"committee",
439-
"delegated",
440-
"metadata",
441-
"bonded",
442-
"unbonding",
443-
"account",
444-
"withdraw",
445-
]);
437+
if (connection.network == "testnet") {
438+
expect(mappings).deep.equal([
439+
"committee",
440+
"delegated",
441+
"metadata",
442+
"bonded",
443+
"unbonding",
444+
"account",
445+
"withdraw",
446+
"pool"
447+
]);
448+
} else {
449+
expect(mappings).deep.equal([
450+
"committee",
451+
"delegated",
452+
"metadata",
453+
"bonded",
454+
"unbonding",
455+
"account",
456+
"withdraw",
457+
]);
458+
}
446459
}
447460
});
448461
});
@@ -589,7 +602,7 @@ describe("NodeConnection", () => {
589602
}
590603
});
591604

592-
it("should have correct data within the wasm object and summary object for a deployment transaction", async () => {
605+
it.skip("should have correct data within the wasm object and summary object for a deployment transaction", async () => {
593606
// Get the deployment transaction for token_registry.aleo
594607
if (connection.network === "mainnet") {
595608
const transaction = await connection.getTransactionObject("at15mwg0jyhvpjjrfxwrlwzn8puusnmy7r3xzvpjht4e5gzgnp68q9qd0qqec");

sdk/tests/program-manager.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
PrivateKey,
99
Program,
1010
ProgramManager,
11+
ProvingRequest,
1112
RecordPlaintext,
1213
Transaction,
1314
verifyFunctionExecution,
@@ -108,6 +109,43 @@ describe('Program Manager', () => {
108109
});
109110

110111
describe('Proving Requests and Authorizations', () => {
112+
it('Should build correct authorizations from Proving Request', async () => {
113+
// Build a proving request for the "spin" function of "puzzle_spinner_v002.aleo".
114+
const provingRequest = await programManager.provingRequest({
115+
programName: PUZZLE_SPINNER_PROGRAM_ID,
116+
functionName: "spin",
117+
baseFee: 1000000,
118+
priorityFee: 0,
119+
privateFee: false,
120+
inputs: [
121+
PUZZLE_SPINNER_V002_INPUT_0,
122+
PUZZLE_SPINNER_V002_INPUT_1,
123+
PUZZLE_SPINNER_V002_INPUT_2,
124+
],
125+
broadcast: false,
126+
privateKey: PrivateKey.from_string(<string>process.env["PUZZLE_PK"])
127+
});
128+
129+
// Ensure serialization methods lead to the expected.
130+
const provingRequestFromString = ProvingRequest.fromString(provingRequest.toString());
131+
const provingRequestFromBytes = ProvingRequest.fromBytesLe(provingRequest.toBytesLe());
132+
133+
// Ensure all authorizations are equal.
134+
expect(provingRequestFromString.equals(provingRequestFromBytes));
135+
expect(provingRequestFromString.equals(provingRequest));
136+
137+
// Ensure the broadcast flag is set to false.
138+
expect(provingRequest.broadcast()).equal(false);
139+
140+
// Get the authorizations.
141+
const authorization = provingRequest.authorization();
142+
const feeAuthorization = <Authorization>provingRequest.feeAuthorization();
143+
144+
// Ensure the authorizations have the correct number of transitions.
145+
expect(authorization.transitions().length).equal(3);
146+
expect(feeAuthorization.transitions().length).equal(1);
147+
})
148+
111149
it('Should build correct authorizations', async () => {
112150
// Build an authorization for the spin function of "puzzle_spinner_v002.aleo".
113151
const authorization = await programManager.buildAuthorization({

wasm/src/programs/manager/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ mod authorize;
1818
mod deploy;
1919
mod execute;
2020
mod join;
21+
mod proving_request;
2122
mod split;
2223
mod transfer;
2324

0 commit comments

Comments
 (0)