Skip to content

Commit 2db8a7d

Browse files
Enable offline transactions
1 parent 6481ccd commit 2db8a7d

File tree

7 files changed

+246
-130
lines changed

7 files changed

+246
-130
lines changed

create-leo-app/template-offline-public-transaction-ts/src/index.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ async function buildTransferPublicTxOffline(recipientAddress: Address, amount: n
4343
// keys into the key manager.
4444
console.log("Inserting proving keys into key provider");
4545
offlineKeyProvider.insertFeePublicKeys(feePublicProvingKey);
46-
offlineKeyProvider.cacheKeys(CREDITS_PROGRAM_KEYS.inclusion.locator, [inclusionKey, VerifyingKey.inclusionVerifier()]);
46+
offlineKeyProvider.insertInclusionKeys(inclusionKey)
4747

4848
try {
4949
offlineKeyProvider.insertTransferPublicKeys(transferPublicProvingKey);
@@ -54,7 +54,7 @@ async function buildTransferPublicTxOffline(recipientAddress: Address, amount: n
5454

5555
// Create an offline query to complete the inclusion proof
5656
let offlineQuery: OfflineQuery;
57-
const blockHeight = 0;
57+
const blockHeight = 9233665;
5858
// TODO this is a placeholder block height for now, which offlineQuery now requires
5959
try {
6060
offlineQuery = new OfflineQuery(blockHeight, latestStateRoot);
@@ -115,7 +115,7 @@ async function buildBondingTxOffline(
115115
offlineKeyProvider.insertBondPublicKeys(bondPublicProvingKey);
116116
offlineKeyProvider.insertUnbondPublicKeys(unBondPublicProvingKey);
117117
offlineKeyProvider.insertClaimUnbondPublicKeys(claimUnbondPublicProvingKey);
118-
offlineKeyProvider.cacheKeys(CREDITS_PROGRAM_KEYS.inclusion.locator, [inclusionProvingKey, VerifyingKey.inclusionVerifier()]);
118+
offlineKeyProvider.insertInclusionKeys(inclusionProvingKey);
119119

120120
// Insert the key provider into the program manager
121121
programManager.setKeyProvider(offlineKeyProvider);
@@ -129,7 +129,7 @@ async function buildBondingTxOffline(
129129

130130
const bondPublicOptions = {
131131
keySearchParams: OfflineSearchParams.bondPublicKeyParams(),
132-
offlineQuery: new OfflineQuery(0, latestStateRoot)
132+
offlineQuery: new OfflineQuery(9233665, latestStateRoot)
133133
};
134134

135135
console.log("Build bond public transaction");
@@ -144,7 +144,7 @@ async function buildBondingTxOffline(
144144

145145
const unbondPublicOptions = {
146146
keySearchParams: OfflineSearchParams.unbondPublicKeyParams(),
147-
offlineQuery: new OfflineQuery(0, latestStateRoot)
147+
offlineQuery: new OfflineQuery(9233665, latestStateRoot)
148148
};
149149

150150
const unBondTx = <Transaction>await programManager.buildUnbondPublicTransaction(
@@ -157,7 +157,7 @@ async function buildBondingTxOffline(
157157
console.log("Building a claim_unbond_public transaction offline");
158158
const claimUnbondPublicOptions = {
159159
keySearchParams: OfflineSearchParams.claimUnbondPublicKeyParams(),
160-
offlineQuery: new OfflineQuery(0, latestStateRoot)
160+
offlineQuery: new OfflineQuery(9233665, latestStateRoot)
161161
};
162162

163163
const claimUnbondTx = <Transaction>await programManager.buildClaimUnbondPublicTransaction(
@@ -185,14 +185,19 @@ async function main() {
185185
// ------------------OFFLINE COMPONENT---------------------
186186
// (Do this part on an offline machine)
187187
// Get the latest state root from an online machine and enter it into an offline machine
188-
const latestStateRoot = "sr1p93gpsezrjzdhcd2wujznx5s07k8qa39t6vfcej35zew8vn2jyrs46te8q";
188+
const latestStateRoot = "sr18kzmy5fw3rwr8gldzfg2deemvyapgkuahte372shyz7phtwtsypqngg0ml";
189189

190-
// Build a transfer_public transaction
190+
// // // Build a transfer_public transaction
191191
const transferTx = await buildTransferPublicTxOffline(stakerAddress, 10000, latestStateRoot, transferKeyPaths);
192192
console.log("Transfer transaction built offline!");
193193
console.log(`\n---------------transfer_public transaction---------------\n${transferTx}`);
194194
console.log(`---------------------------------------------------------`);
195195

196+
function sleep(ms) {
197+
return new Promise(resolve => setTimeout(resolve, ms));
198+
}
199+
await sleep(15000);
200+
196201
// Build bonding & unbonding transactions
197202
const bondTransactions = await buildBondingTxOffline(validatorAddress, withdrawalAddress, 100, latestStateRoot, bondingKeyPaths);
198203
console.log("Bonding transactions built offline!");

sdk/src/function-key-provider.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,13 @@ interface FunctionKeyProvider {
173173
*/
174174
feePublicKeys(): Promise<FunctionKeyPair>;
175175

176+
/**
177+
* Get keys for the inclusion proof.
178+
*
179+
* @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the join function
180+
*/
181+
inclusionKeys(): Promise<FunctionKeyPair>;
182+
176183
/**
177184
* Get join function keys from the credits.aleo program
178185
*
@@ -515,6 +522,24 @@ class AleoKeyProvider implements FunctionKeyProvider {
515522
}
516523
}
517524

525+
/**
526+
* Returns the proving and verifying keys for the transfer_public function.
527+
*
528+
* @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the transfer_public function
529+
*/
530+
async transferPublicKeys(): Promise<FunctionKeyPair> {
531+
return await this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.transfer_public);
532+
}
533+
534+
/**
535+
* Returns the proving and verifying keys for the inclusion proof.
536+
*
537+
* @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the inclusion proof.
538+
*/
539+
async inclusionKeys(): Promise<FunctionKeyPair> {
540+
return await this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.inclusion);
541+
}
542+
518543
/**
519544
* Returns the proving and verifying keys for the join function in the credits.aleo program
520545
*

sdk/src/offline-key-provider.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,15 @@ class OfflineKeyProvider implements FunctionKeyProvider {
361361
return this.functionKeys(OfflineSearchParams.feePublicKeyParams());
362362
};
363363

364+
/**
365+
* Get the inclusion prover keys from. The keys must be cached prior to calling this method for it to work.
366+
*
367+
* @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the inclusion prover
368+
*/
369+
inclusionKeys(): Promise<FunctionKeyPair> {
370+
return this.functionKeys(OfflineSearchParams.inclusionKeyParams());
371+
};
372+
364373
/**
365374
* Get join function keys from the credits.aleo program. The keys must be cached prior to calling this
366375
* method for it to work.
@@ -488,6 +497,21 @@ class OfflineKeyProvider implements FunctionKeyProvider {
488497
}
489498
}
490499

500+
/**
501+
* Insert the proving and verifying keys for the inclusion prover into the cache. Only the proving key needs
502+
* to be inserted, the verifying key is automatically inserted by the SDK. This function will automatically check
503+
* that the keys match the expected checksum for the inclusion prover.
504+
*
505+
* @param provingKey
506+
*/
507+
insertInclusionKeys(provingKey: ProvingKey) {
508+
if (provingKey.isInclusionProver()) {
509+
this.cache.set(CREDITS_PROGRAM_KEYS.inclusion.locator, [provingKey.toBytes(), VerifyingKey.inclusionVerifier().toBytes()]);
510+
} else {
511+
throw new Error("Attempted to insert invalid proving keys for the inclusion prover");
512+
}
513+
}
514+
491515
/**
492516
* Insert the proving and verifying keys for the join function into the cache. Only the proving key needs
493517
* to be inserted, the verifying key is automatically inserted by the SDK. This function will automatically check

sdk/src/program-manager.ts

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ class ProgramManager {
116116
host: string;
117117
networkClient: AleoNetworkClient;
118118
recordProvider: RecordProvider | undefined;
119+
inclusionKeysLoaded: boolean = false;
119120

120121
/** Create a new instance of the ProgramManager
121122
*
@@ -584,10 +585,15 @@ class ProgramManager {
584585
}
585586
}
586587

587-
if (offlineQuery) {
588-
console.log("Using offline query.");
589-
} else {
590-
console.log("Not using offline query.");
588+
if (offlineQuery && !this.inclusionKeysLoaded) {
589+
try {
590+
const inclusionKeys = await this.keyProvider.inclusionKeys();
591+
WasmProgramManager.loadInclusionProver(inclusionKeys[0])
592+
this.inclusionKeysLoaded = true;
593+
console.log("Successfully loaded inclusion key");
594+
} catch {
595+
logAndThrow(`Inclusion key bytes not loaded, please ensure the program manager is initialized with a KeyProvider that includes the inclusion key.`)
596+
}
591597
}
592598

593599
// Build an execution transaction
@@ -1024,6 +1030,18 @@ class ProgramManager {
10241030
);
10251031
}
10261032

1033+
// Load the inclusion prover offline.
1034+
if (offlineQuery && !this.inclusionKeysLoaded) {
1035+
try {
1036+
const inclusionKeys = await this.keyProvider.inclusionKeys();
1037+
WasmProgramManager.loadInclusionProver(inclusionKeys[0])
1038+
this.inclusionKeysLoaded = true;
1039+
console.log("Successfully loaded inclusion key");
1040+
} catch {
1041+
logAndThrow(`Inclusion key bytes not loaded, please ensure the program manager is initialized with a KeyProvider that includes the inclusion key.`)
1042+
}
1043+
}
1044+
10271045
// Build an execution transaction and submit it to the network
10281046
const tx = await WasmProgramManager.buildJoinTransaction(
10291047
executionPrivateKey,
@@ -1116,6 +1134,18 @@ class ProgramManager {
11161134
);
11171135
}
11181136

1137+
// Load the inclusion prover offline.
1138+
if (offlineQuery && !this.inclusionKeysLoaded) {
1139+
try {
1140+
const inclusionKeys = await this.keyProvider.inclusionKeys();
1141+
WasmProgramManager.loadInclusionProver(inclusionKeys[0])
1142+
this.inclusionKeysLoaded = true;
1143+
console.log("Successfully loaded inclusion key");
1144+
} catch {
1145+
logAndThrow(`Inclusion key bytes not loaded, please ensure the program manager is initialized with a KeyProvider that includes the inclusion key.`)
1146+
}
1147+
}
1148+
11191149
// Build an execution transaction and submit it to the network
11201150
const tx = await WasmProgramManager.buildSplitTransaction(
11211151
executionPrivateKey,
@@ -1294,10 +1324,18 @@ class ProgramManager {
12941324
);
12951325
}
12961326

1297-
if (offlineQuery) {
1298-
console.log("Using offline query.");
1299-
} else {
1300-
console.log("Using online query.")
1327+
// Load the inclusion prover offline.
1328+
if (offlineQuery && !this.inclusionKeysLoaded) {
1329+
const inclusionKeys = await this.keyProvider.inclusionKeys();
1330+
WasmProgramManager.loadInclusionProver(inclusionKeys[0])
1331+
try {
1332+
const inclusionKeys = await this.keyProvider.inclusionKeys();
1333+
WasmProgramManager.loadInclusionProver(inclusionKeys[0])
1334+
this.inclusionKeysLoaded = true;
1335+
console.log("Successfully loaded inclusion key");
1336+
} catch {
1337+
logAndThrow(`Inclusion key bytes not loaded, please ensure the program manager is initialized with a KeyProvider that includes the inclusion key.`)
1338+
}
13011339
}
13021340

13031341
// Build an execution transaction
@@ -2223,6 +2261,15 @@ class ProgramManager {
22232261
}
22242262
}
22252263

2264+
/**
2265+
* Set the inclusion key bytes.
2266+
*
2267+
* @param {executionResponse} executionResponse The response from an offline function execution (via the `programManager.run` method)
2268+
* @param {ImportedPrograms} imports The imported programs used in the execution. Specified as { "programName": "programSourceCode", ... }
2269+
* @param {ImportedVerifyingKeys} importedVerifyingKeys The verifying keys in the execution. Specified as { "programName": [["functionName", "verifyingKey"], ...], ... }
2270+
* @returns {boolean} True if the proof is valid, false otherwise
2271+
*
2272+
22262273
/**
22272274
* Create a program object from a program's source code
22282275
*

0 commit comments

Comments
 (0)