Skip to content

Commit

Permalink
Merge pull request #687 from LIT-Protocol/LIT-3959-raw-wrapped-keys-l…
Browse files Browse the repository at this point in the history
…itaction-functions

LIT-3959 - Export raw wrapped-keys LIT action functions
  • Loading branch information
Ansonhkg authored Oct 18, 2024
2 parents 2211187 + 141f0a0 commit c2cfb89
Show file tree
Hide file tree
Showing 39 changed files with 468 additions and 330 deletions.
16 changes: 8 additions & 8 deletions packages/wrapped-keys-lit-actions/esbuild.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@ module.exports = {
(async () => {
await esbuild.build({
entryPoints: [
'./src/lib/solana/signTransactionWithEncryptedSolanaKey.js',
'./src/lib/solana/signMessageWithEncryptedSolanaKey.js',
'./src/lib/solana/generateEncryptedSolanaPrivateKey.js',
'./src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js',
'./src/lib/ethereum/signMessageWithEncryptedEthereumKey.js',
'./src/lib/ethereum/generateEncryptedEthereumPrivateKey.js',
'./src/lib/common/exportPrivateKey.js',
'./src/lib/common/batchGenerateEncryptedKeys.js',
'./src/lib/self-executing-actions/solana/signTransactionWithEncryptedSolanaKey.js',
'./src/lib/self-executing-actions/solana/signMessageWithEncryptedSolanaKey.js',
'./src/lib/self-executing-actions/solana/generateEncryptedSolanaPrivateKey.js',
'./src/lib/self-executing-actions/ethereum/signTransactionWithEncryptedEthereumKey.js',
'./src/lib/self-executing-actions/ethereum/signMessageWithEncryptedEthereumKey.js',
'./src/lib/self-executing-actions/ethereum/generateEncryptedEthereumPrivateKey.js',
'./src/lib/self-executing-actions/common/exportPrivateKey.js',
'./src/lib/self-executing-actions/common/batchGenerateEncryptedKeys.js',
],
bundle: true,
minify: true,
Expand Down
5 changes: 5 additions & 0 deletions packages/wrapped-keys-lit-actions/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as signTransactionWithEthereumEncryptedKey from './generated/ethereum/s
import * as generateEncryptedSolanaPrivateKey from './generated/solana/generateEncryptedSolanaPrivateKey';
import * as signMessageWithSolanaEncryptedKey from './generated/solana/signMessageWithEncryptedSolanaKey';
import * as signTransactionWithSolanaEncryptedKey from './generated/solana/signTransactionWithEncryptedSolanaKey';
import { rawActionFunctions } from './lib/raw-action-functions';

import type {
LitActionCodeRepository,
Expand Down Expand Up @@ -36,6 +37,10 @@ const litActionRepositoryCommon: LitActionCodeRepositoryCommon = {
};

export {
// Raw functions, <not wrapped in IIFEs>, for consumers to be able to compose these into their own LIT actions
// Facilitates running e.g. `batchGenerateEncryptedKeys` using `Lit.Actions.runOnce` inside of another action
rawActionFunctions,

// Individual exports to allow tree-shaking and only importing the lit actions you need
batchGenerateEncryptedKeys,
exportPrivateKey,
Expand Down
9 changes: 9 additions & 0 deletions packages/wrapped-keys-lit-actions/src/lib/abortError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class AbortError extends Error {
name = 'AbortError';
}

export const rethrowIfAbortError = (err) => {
if (err instanceof AbortError) {
throw err;
}
};

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* global Lit */

import { AbortError } from '../../abortError';
import { removeSaltFromDecryptedKey } from '../../utils';

async function tryDecryptToSingleNode({
accessControlConditions,
ciphertext,
dataToEncryptHash,
}) {
try {
// May be undefined, since we're using `decryptToSingleNode`
return await Lit.Actions.decryptToSingleNode({
accessControlConditions,
ciphertext,
dataToEncryptHash,
chain: 'ethereum',
authSig: null,
});
} catch (err) {
throw new Error(`When decrypting key to a single node - ${err.message}`);
}
}

export async function getDecryptedKeyToSingleNode({
accessControlConditions,
ciphertext,
dataToEncryptHash,
}) {
const decryptedPrivateKey = await tryDecryptToSingleNode({
accessControlConditions,
ciphertext,
dataToEncryptHash,
});

if (!decryptedPrivateKey) {
// Silently exit on nodes which didn't run the `decryptToSingleNode` code
throw new AbortError();
}

return removeSaltFromDecryptedKey(decryptedPrivateKey);
}
25 changes: 25 additions & 0 deletions packages/wrapped-keys-lit-actions/src/lib/litActionHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* global Lit */

import { AbortError } from './abortError';

export async function litActionHandler(actionFunc) {
try {
const litActionResult = await actionFunc();
// Don't re-stringify a string; we don't want to double-escape it
const response =
typeof litActionResult === 'string'
? litActionResult
: JSON.stringify(litActionResult);

Lit.Actions.setResponse({ response });
} catch (err) {
// AbortError means exit immediately and do _NOT_ set a response
// Nested code should really only throw this in cases where using e.g. `decryptToSingleNode`
// And this execution isn't that node.
if (err instanceof AbortError) {
return;
}

Lit.Actions.setResponse({ response: `Error: ${err.message}` });
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
const { encryptPrivateKey } = require('./internal/encryptKey');
const { encryptPrivateKey } = require('../../internal/common/encryptKey');
const {
generateEthereumPrivateKey,
} = require('../ethereum/internal/generatePrivateKey');
const { signMessageEthereumKey } = require('../ethereum/internal/signMessage');
} = require('../../internal/ethereum/generatePrivateKey');
const {
signMessageEthereumKey,
} = require('../../internal/ethereum/signMessage');
const {
generateSolanaPrivateKey,
} = require('../solana/internal/generatePrivateKey');
const { signMessageSolanaKey } = require('../solana/internal/signMessage');

/* global accessControlConditions, actions, Lit*/
} = require('../../internal/solana/generatePrivateKey');
const { signMessageSolanaKey } = require('../../internal/solana/signMessage');

async function processEthereumAction(action) {
async function processEthereumAction({ action, accessControlConditions }) {
const { network, generateKeyParams } = action;
const messageToSign = action.signMessageParams?.messageToSign;

Expand Down Expand Up @@ -42,7 +42,7 @@ async function processEthereumAction(action) {
};
}

async function processSolanaAction(action) {
async function processSolanaAction({ action, accessControlConditions }) {
const { network, generateKeyParams } = action;

const messageToSign = action.signMessageParams?.messageToSign;
Expand Down Expand Up @@ -75,15 +75,21 @@ async function processSolanaAction(action) {
};
}

async function processActions(actions) {
async function processActions({ actions, accessControlConditions }) {
return Promise.all(
actions.map(async (action, ndx) => {
const { network } = action;

if (network === 'evm') {
return await processEthereumAction(action, ndx);
return await processEthereumAction({
action,
accessControlConditions,
});
} else if (network === 'solana') {
return await processSolanaAction(action, ndx);
return await processSolanaAction({
action,
accessControlConditions,
});
} else {
// Just in case :tm:
throw new Error(`Invalid network for action[${ndx}]: ${network}`);
Expand Down Expand Up @@ -128,20 +134,14 @@ function validateParams(actions) {
});
}

(async () => {
try {
validateParams(actions);

const batchGeneratePrivateKeysActionResult = await processActions(actions);

Lit.Actions.setResponse({
response: JSON.stringify(batchGeneratePrivateKeysActionResult),
});
export async function batchGenerateEncryptedKeys({
actions,
accessControlConditions,
}) {
validateParams(actions);

// 1. Generate both EVM and solana private keys
// 2. Run appropriate signMessage for each key _and_ encrypt the keys for persistence to wrapped-keys backend
// 3. Return results for both signMessage ops and both encrypted key payloads for persistence
} catch (err) {
Lit.Actions.setResponse({ response: `Error: ${err.message}` });
}
})();
return processActions({
actions,
accessControlConditions,
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const {
getDecryptedKeyToSingleNode,
} = require('../../internal/common/getDecryptedKeyToSingleNode');

/**
*
* Exports the private key after decrypting and removing the salt from it.
*
* @jsParam pkpAddress - The Eth address of the PKP which is associated with the Wrapped Key
* @jsParam ciphertext - For the encrypted Wrapped Key
* @jsParam dataToEncryptHash - For the encrypted Wrapped Key
* @jsParam accessControlConditions - The access control condition that allows only the pkpAddress to decrypt the Wrapped Key
*
* @returns { Promise<string> } - Returns a decrypted private key.
*/

export async function exportPrivateKey({
accessControlConditions,
ciphertext,
dataToEncryptHash,
}) {
return getDecryptedKeyToSingleNode({
accessControlConditions,
ciphertext,
dataToEncryptHash,
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
*
* Generates a random Ethers private key and only allows the provided PKP to decrypt it
*
* @jsParam pkpAddress - The Eth address of the PKP which is associated with the Wrapped Key
* @jsParam accessControlConditions - The access control condition that allows only the pkpAddress to decrypt the Wrapped Key
*
* @returns { Promise<{ciphertext: string, dataToEncryptHash: string, publicKey: string}> } - Returns object with ciphertext & dataToEncryptHash which are the result of the encryption. Also returns the publicKey of the newly generated Ethers Wrapped Key.
*/
import { encryptPrivateKey } from '../../internal/common/encryptKey';
import { generateEthereumPrivateKey } from '../../internal/ethereum/generatePrivateKey';

export async function generateEncryptedEthereumPrivateKey({
accessControlConditions,
}) {
const { privateKey, publicKey } = generateEthereumPrivateKey();
return encryptPrivateKey({
accessControlConditions,
privateKey,
publicKey,
});
}
Loading

0 comments on commit c2cfb89

Please sign in to comment.