Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"build": "rimraf dist/js && rollup --config",
"dev": "npm run build && node dist/index.js"
"dev": "npm run build && node --trace-uncaught dist/index.js"
},
"dependencies": {
"@provablehq/sdk": "^0.9.4"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ async function preDownloadTransferKeys() {
const keysDirPath = path.join(__dirname, "keys");
await fsPromises.mkdir(keysDirPath, { recursive: true });

for (const keyData of [CREDITS_PROGRAM_KEYS.transfer_public, CREDITS_PROGRAM_KEYS.fee_public, CREDITS_PROGRAM_KEYS.transfer_public_as_signer]) {
for (const keyData of [CREDITS_PROGRAM_KEYS.transfer_public, CREDITS_PROGRAM_KEYS.fee_public, CREDITS_PROGRAM_KEYS.transfer_public_as_signer, CREDITS_PROGRAM_KEYS.inclusion]) {
try {
keyPaths[keyData.locator] = await downloadAndSaveKey(keyData, keysDirPath);
} catch (error) {
Expand All @@ -45,7 +45,7 @@ async function preDownloadBondingKeys() {
const keysDirPath = path.join(__dirname, "keys");
await fsPromises.mkdir(keysDirPath, { recursive: true });

for (const keyData of [CREDITS_PROGRAM_KEYS.bond_public, CREDITS_PROGRAM_KEYS.fee_public, CREDITS_PROGRAM_KEYS.unbond_public, CREDITS_PROGRAM_KEYS.claim_unbond_public]) {
for (const keyData of [CREDITS_PROGRAM_KEYS.bond_public, CREDITS_PROGRAM_KEYS.fee_public, CREDITS_PROGRAM_KEYS.unbond_public, CREDITS_PROGRAM_KEYS.claim_unbond_public, CREDITS_PROGRAM_KEYS.inclusion]) {
try {
keyPaths[keyData.locator] = await downloadAndSaveKey(keyData, keysDirPath);
} catch (error) {
Expand Down
129 changes: 81 additions & 48 deletions create-leo-app/template-offline-public-transaction-ts/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
import {Account, Address, CREDITS_PROGRAM_KEYS, initThreadPool, ProgramManager, OfflineQuery, OfflineKeyProvider, OfflineSearchParams, ProvingKey, Transaction} from "@provablehq/sdk";
import {
Account,
Address,
CREDITS_PROGRAM_KEYS,
initThreadPool,
ProgramManager,
OfflineQuery,
OfflineKeyProvider,
OfflineSearchParams,
ProvingKey,
Transaction,
VerifyingKey
} from "@provablehq/sdk";
import { getLocalKey, preDownloadBondingKeys, preDownloadTransferKeys } from "./helpers";

await initThreadPool();
Expand All @@ -19,6 +31,9 @@ async function buildTransferPublicTxOffline(recipientAddress: Address, amount: n
const transferPublicProvingKey = ProvingKey.fromBytes(
await getLocalKey(<string>keyPaths[CREDITS_PROGRAM_KEYS.transfer_public.locator])
);
const inclusionKey = ProvingKey.fromBytes(
await getLocalKey(<string>keyPaths[CREDITS_PROGRAM_KEYS.inclusion.locator])
);

// Create an offline key provider
console.log("Creating offline key provider");
Expand All @@ -28,25 +43,25 @@ async function buildTransferPublicTxOffline(recipientAddress: Address, amount: n
// keys into the key manager.
console.log("Inserting proving keys into key provider");
offlineKeyProvider.insertFeePublicKeys(feePublicProvingKey);
offlineKeyProvider.insertInclusionKeys(inclusionKey)

try {
offlineKeyProvider.insertTransferPublicKeys(transferPublicProvingKey);
console.log("Successfully inserted proving key");
} catch (err) {
console.error("Failed to insert proving key:", err);
}

try {
offlineKeyProvider.insertTransferPublicKeys(transferPublicProvingKey);
console.log("Successfully inserted proving key");
} catch (err) {
console.error("Failed to insert proving key:", err);
}

// Create an offline query to complete the inclusion proof
let offlineQuery: OfflineQuery;
const blockHeight = 0;
const blockHeight = 9233665;
// TODO this is a placeholder block height for now, which offlineQuery now requires
try {
const offlineQuery = new OfflineQuery(blockHeight, latestStateRoot);
console.log("Successfully created OfflineQuery", offlineQuery);
} catch (err) {
console.error("Failed to create OfflineQuery:", err);
}
try {
offlineQuery = new OfflineQuery(blockHeight, latestStateRoot);
console.log("Successfully created OfflineQuery", offlineQuery);
} catch (err) {
console.error("Failed to create OfflineQuery:", err);
}

// Insert the key provider into the program manager
programManager.setKeyProvider(offlineKeyProvider);
Expand Down Expand Up @@ -83,10 +98,12 @@ async function buildBondingTxOffline(
const bondPublicKeyBytes = await getLocalKey(<string>keyPaths[CREDITS_PROGRAM_KEYS.bond_public.locator]);
const unbondPublicKeyBytes = await getLocalKey(<string>keyPaths[CREDITS_PROGRAM_KEYS.unbond_public.locator]);
const claimUnbondPublicKeyBytes = await getLocalKey(<string>keyPaths[CREDITS_PROGRAM_KEYS.claim_unbond_public.locator]);
const inclusionKeys = await getLocalKey(<string>keyPaths[CREDITS_PROGRAM_KEYS.inclusion.locator]);
const feePublicProvingKey = ProvingKey.fromBytes(feePublicKeyBytes);
const bondPublicProvingKey = ProvingKey.fromBytes(bondPublicKeyBytes);
const unBondPublicProvingKey = ProvingKey.fromBytes(unbondPublicKeyBytes);
const claimUnbondPublicProvingKey = ProvingKey.fromBytes(claimUnbondPublicKeyBytes);
const inclusionProvingKey = ProvingKey.fromBytes(inclusionKeys);

// Create an offline key provider to fetch keys without connection to the internet
console.log("Creating offline key provider");
Expand All @@ -98,6 +115,7 @@ async function buildBondingTxOffline(
offlineKeyProvider.insertBondPublicKeys(bondPublicProvingKey);
offlineKeyProvider.insertUnbondPublicKeys(unBondPublicProvingKey);
offlineKeyProvider.insertClaimUnbondPublicKeys(claimUnbondPublicProvingKey);
offlineKeyProvider.insertInclusionKeys(inclusionProvingKey);

// Insert the key provider into the program manager
programManager.setKeyProvider(offlineKeyProvider);
Expand All @@ -111,10 +129,10 @@ async function buildBondingTxOffline(

const bondPublicOptions = {
keySearchParams: OfflineSearchParams.bondPublicKeyParams(),
offlineQuery: new OfflineQuery(0, latestStateRoot)
offlineQuery: new OfflineQuery(9233665, latestStateRoot)
};


console.log("Build bond public transaction");
const bondTx = <Transaction>await programManager.buildBondPublicTransaction(
validatorAddress.to_string(),
withdrawalAddress.to_string(),
Expand All @@ -126,7 +144,7 @@ async function buildBondingTxOffline(

const unbondPublicOptions = {
keySearchParams: OfflineSearchParams.unbondPublicKeyParams(),
offlineQuery: new OfflineQuery(0, latestStateRoot)
offlineQuery: new OfflineQuery(9233665, latestStateRoot)
};

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

const claimUnbondTx = <Transaction>await programManager.buildClaimUnbondPublicTransaction(
Expand All @@ -150,38 +168,53 @@ async function buildBondingTxOffline(
return [bondTx, unBondTx, claimUnbondTx];
}

// -------------------ONLINE COMPONENT---------------------
// (Do this part on an internet connected machine)

// Download the needed keys for the functions we want to execute offline
const transferKeyPaths = await preDownloadTransferKeys();
const bondingKeyPaths = await preDownloadBondingKeys();
//---------------------------------------------------------

// ------------------OFFLINE COMPONENT---------------------
// (Do this part on an offline machine)
// Get the latest state root from an online machine and enter it into an offline machine
const latestStateRoot = "sr1p93gpsezrjzdhcd2wujznx5s07k8qa39t6vfcej35zew8vn2jyrs46te8q";

// Build a transfer_public transaction
const stakerAddress = new Account().address();
const validatorAddress = new Account().address();
const withdrawalAddress = new Account().address();
const transferTx = await buildTransferPublicTxOffline(stakerAddress, 10000, latestStateRoot, transferKeyPaths);
console.log("Transfer transaction built offline!");
console.log(`\n---------------transfer_public transaction---------------\n${transferTx}`);
console.log(`---------------------------------------------------------`);

// Build bonding & unbonding transactions
const bondTransactions = await buildBondingTxOffline(validatorAddress, withdrawalAddress, 100, latestStateRoot, bondingKeyPaths);
console.log("Bonding transactions built offline!");
console.log(`\n-----------------bond_public transaction-----------------\n${bondTransactions[0]}`);
console.log(`---------------------------------------------------------`);
console.log(`\n----------------unbond_public transaction:---------------\n${bondTransactions[1]}`);
console.log(`---------------------------------------------------------`);
console.log(`\n-----------------claim_unbond_public transaction:---------------\n${bondTransactions[2]}`);
console.log(`---------------------------------------------------------`);
//---------------------------------------------------------

async function main() {
try {
// -------------------ONLINE COMPONENT---------------------
// (Do this part on an internet connected machine)

// Download the needed keys for the functions we want to execute offline
const transferKeyPaths = await preDownloadTransferKeys();
const bondingKeyPaths = await preDownloadBondingKeys();
//---------------------------------------------------------

// ------------------OFFLINE COMPONENT---------------------
// (Do this part on an offline machine)
// Get the latest state root from an online machine and enter it into an offline machine
const latestStateRoot = "sr18kzmy5fw3rwr8gldzfg2deemvyapgkuahte372shyz7phtwtsypqngg0ml";

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

function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
await sleep(15000);

// Build bonding & unbonding transactions
const bondTransactions = await buildBondingTxOffline(validatorAddress, withdrawalAddress, 100, latestStateRoot, bondingKeyPaths);
console.log("Bonding transactions built offline!");
console.log(`\n-----------------bond_public transaction-----------------\n${bondTransactions[0]}`);
console.log(`---------------------------------------------------------`);
console.log(`\n----------------unbond_public transaction:---------------\n${bondTransactions[1]}`);
console.log(`---------------------------------------------------------`);
console.log(`\n-----------------claim_unbond_public transaction:---------------\n${bondTransactions[2]}`);
console.log(`---------------------------------------------------------`);
//---------------------------------------------------------
} catch (e) {
console.log(e);
process.exit(1);
}
}

await main();

// -------------------ONLINE COMPONENT---------------------
// (Do this part on an internet connected machine)
Expand Down
4 changes: 2 additions & 2 deletions sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@provablehq/sdk",
"version": "0.9.4",
"version": "0.9.4-offline-rc",
"description": "A Software Development Kit (SDK) for Zero-Knowledge Transactions",
"collaborators": [
"The Provable Team"
Expand Down Expand Up @@ -47,7 +47,7 @@
},
"homepage": "https://github.com/ProvableHQ/sdk#readme",
"dependencies": {
"@provablehq/wasm": "^0.9.4",
"@provablehq/wasm": "0.9.4-offline-rc",
"comlink": "^4.4.2",
"core-js": "^3.40.0",
"mime": "^4.0.6",
Expand Down
25 changes: 25 additions & 0 deletions sdk/src/function-key-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,13 @@ interface FunctionKeyProvider {
*/
feePublicKeys(): Promise<FunctionKeyPair>;

/**
* Get keys for the inclusion proof.
*
* @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the join function
*/
inclusionKeys(): Promise<FunctionKeyPair>;

/**
* Get join function keys from the credits.aleo program
*
Expand Down Expand Up @@ -515,6 +522,24 @@ class AleoKeyProvider implements FunctionKeyProvider {
}
}

/**
* Returns the proving and verifying keys for the transfer_public function.
*
* @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the transfer_public function
*/
async transferPublicKeys(): Promise<FunctionKeyPair> {
return await this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.transfer_public);
}

/**
* Returns the proving and verifying keys for the inclusion proof.
*
* @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the inclusion proof.
*/
async inclusionKeys(): Promise<FunctionKeyPair> {
return await this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.inclusion);
}

/**
* Returns the proving and verifying keys for the join function in the credits.aleo program
*
Expand Down
24 changes: 24 additions & 0 deletions sdk/src/offline-key-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,15 @@ class OfflineKeyProvider implements FunctionKeyProvider {
return this.functionKeys(OfflineSearchParams.feePublicKeyParams());
};

/**
* Get the inclusion prover keys from. The keys must be cached prior to calling this method for it to work.
*
* @returns {Promise<FunctionKeyPair>} Proving and verifying keys for the inclusion prover
*/
inclusionKeys(): Promise<FunctionKeyPair> {
return this.functionKeys(OfflineSearchParams.inclusionKeyParams());
};

/**
* Get join function keys from the credits.aleo program. The keys must be cached prior to calling this
* method for it to work.
Expand Down Expand Up @@ -488,6 +497,21 @@ class OfflineKeyProvider implements FunctionKeyProvider {
}
}

/**
* Insert the proving and verifying keys for the inclusion prover 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 the inclusion prover.
*
* @param provingKey
*/
insertInclusionKeys(provingKey: ProvingKey) {
if (provingKey.isInclusionProver()) {
this.cache.set(CREDITS_PROGRAM_KEYS.inclusion.locator, [provingKey.toBytes(), VerifyingKey.inclusionVerifier().toBytes()]);
} else {
throw new Error("Attempted to insert invalid proving keys for the inclusion prover");
}
}

/**
* 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
Expand Down
Loading
Loading