diff --git a/packages/wrapped-keys-lit-actions/esbuild.config.js b/packages/wrapped-keys-lit-actions/esbuild.config.js index 7b0ba54732..52e2d6f368 100644 --- a/packages/wrapped-keys-lit-actions/esbuild.config.js +++ b/packages/wrapped-keys-lit-actions/esbuild.config.js @@ -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, diff --git a/packages/wrapped-keys-lit-actions/src/index.ts b/packages/wrapped-keys-lit-actions/src/index.ts index 8ca684ec8c..5e8f5427b0 100644 --- a/packages/wrapped-keys-lit-actions/src/index.ts +++ b/packages/wrapped-keys-lit-actions/src/index.ts @@ -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, @@ -36,6 +37,10 @@ const litActionRepositoryCommon: LitActionCodeRepositoryCommon = { }; export { + // Raw functions, , 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, diff --git a/packages/wrapped-keys-lit-actions/src/lib/abortError.js b/packages/wrapped-keys-lit-actions/src/lib/abortError.js new file mode 100644 index 0000000000..189c782bd4 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/abortError.js @@ -0,0 +1,9 @@ +export class AbortError extends Error { + name = 'AbortError'; +} + +export const rethrowIfAbortError = (err) => { + if (err instanceof AbortError) { + throw err; + } +}; diff --git a/packages/wrapped-keys-lit-actions/src/lib/common/exportPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/common/exportPrivateKey.js deleted file mode 100644 index c99cb7280b..0000000000 --- a/packages/wrapped-keys-lit-actions/src/lib/common/exportPrivateKey.js +++ /dev/null @@ -1,38 +0,0 @@ -const { - getDecryptedKeyToSingleNode, -} = require('./internal/getDecryptedKeyToSingleNode'); -const { removeSaltFromDecryptedKey } = require('../utils'); - -/* global accessControlConditions, ciphertext, dataToEncryptHash, Lit */ - -/** - * - * 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 } - Returns a decrypted private key. - */ - -(async () => { - try { - const decryptedPrivateKey = await getDecryptedKeyToSingleNode({ - accessControlConditions, - ciphertext, - dataToEncryptHash, - }); - - if (!decryptedPrivateKey) { - // Silently exit on nodes which didn't run the `decryptToSingleNode` code - return; - } - - const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); - Lit.Actions.setResponse({ response: privateKey }); - } catch (err) { - Lit.Actions.setResponse({ response: `Error: ${err.message}` }); - } -})(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKeyToSingleNode.js b/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKeyToSingleNode.js deleted file mode 100644 index 9f94fb0d02..0000000000 --- a/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKeyToSingleNode.js +++ /dev/null @@ -1,20 +0,0 @@ -/* global Lit */ - -export async function getDecryptedKeyToSingleNode({ - 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}`); - } -} diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js deleted file mode 100644 index 2d6bc44f21..0000000000 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js +++ /dev/null @@ -1,26 +0,0 @@ -/* global accessControlConditions, Lit */ - -/** - * - * 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 } - Returns a stringified JSON object with ciphertext & dataToEncryptHash which are the result of the encryption. Also returns the publicKey of the newly generated Ethers Wrapped Key. - */ -import { generateEthereumPrivateKey } from './internal/generatePrivateKey'; -import { encryptPrivateKey } from '../common/internal/encryptKey'; - -(async () => { - const { privateKey, publicKey } = generateEthereumPrivateKey(); - const encryptedKeyResult = await encryptPrivateKey({ - accessControlConditions, - privateKey, - publicKey, - }); - - Lit.Actions.setResponse({ - response: JSON.stringify(encryptedKeyResult), - }); -})(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js deleted file mode 100644 index 6ae679ea2d..0000000000 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js +++ /dev/null @@ -1,45 +0,0 @@ -const { signMessageEthereumKey } = require('./internal/signMessage'); -const { - getDecryptedKeyToSingleNode, -} = require('../common/internal/getDecryptedKeyToSingleNode'); -const { removeSaltFromDecryptedKey } = require('../utils'); - -/* global accessControlConditions, ciphertext, dataToEncryptHash, messageToSign, Lit */ - -/** - * Signs a message with the Ethers wallet which is also decrypted inside the Lit Action. - * - * @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 messageToSign - The unsigned message to be signed by the Wrapped Key - * @jsParam accessControlConditions - The access control condition that allows only the pkpAddress to decrypt the Wrapped Key - * - * @returns { Promise } - Returns a message signed by the Ethers Wrapped key. Or returns errors if any. - */ - -(async () => { - try { - const decryptedPrivateKey = await getDecryptedKeyToSingleNode({ - accessControlConditions, - ciphertext, - dataToEncryptHash, - }); - - if (!decryptedPrivateKey) { - // Silently exit on nodes which didn't run the `decryptToSingleNode` code - return; - } - - const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); - - const signature = await signMessageEthereumKey({ - privateKey, - messageToSign, - }); - - Lit.Actions.setResponse({ response: signature }); - } catch (err) { - Lit.Actions.setResponse({ response: `Error: ${err.message}` }); - } -})(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/common/internal/encryptKey.js b/packages/wrapped-keys-lit-actions/src/lib/internal/common/encryptKey.js similarity index 100% rename from packages/wrapped-keys-lit-actions/src/lib/common/internal/encryptKey.js rename to packages/wrapped-keys-lit-actions/src/lib/internal/common/encryptKey.js diff --git a/packages/wrapped-keys-lit-actions/src/lib/internal/common/getDecryptedKeyToSingleNode.js b/packages/wrapped-keys-lit-actions/src/lib/internal/common/getDecryptedKeyToSingleNode.js new file mode 100644 index 0000000000..ae2924c75d --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/internal/common/getDecryptedKeyToSingleNode.js @@ -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); +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generatePrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/internal/ethereum/generatePrivateKey.js similarity index 100% rename from packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generatePrivateKey.js rename to packages/wrapped-keys-lit-actions/src/lib/internal/ethereum/generatePrivateKey.js diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessage.js b/packages/wrapped-keys-lit-actions/src/lib/internal/ethereum/signMessage.js similarity index 100% rename from packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessage.js rename to packages/wrapped-keys-lit-actions/src/lib/internal/ethereum/signMessage.js diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransaction.js b/packages/wrapped-keys-lit-actions/src/lib/internal/ethereum/signTransaction.js similarity index 100% rename from packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransaction.js rename to packages/wrapped-keys-lit-actions/src/lib/internal/ethereum/signTransaction.js diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generatePrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/internal/solana/generatePrivateKey.js similarity index 100% rename from packages/wrapped-keys-lit-actions/src/lib/solana/internal/generatePrivateKey.js rename to packages/wrapped-keys-lit-actions/src/lib/internal/solana/generatePrivateKey.js diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessage.js b/packages/wrapped-keys-lit-actions/src/lib/internal/solana/signMessage.js similarity index 100% rename from packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessage.js rename to packages/wrapped-keys-lit-actions/src/lib/internal/solana/signMessage.js diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransaction.js b/packages/wrapped-keys-lit-actions/src/lib/internal/solana/signTransaction.js similarity index 100% rename from packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransaction.js rename to packages/wrapped-keys-lit-actions/src/lib/internal/solana/signTransaction.js diff --git a/packages/wrapped-keys-lit-actions/src/lib/litActionHandler.js b/packages/wrapped-keys-lit-actions/src/lib/litActionHandler.js new file mode 100644 index 0000000000..68a306e8f9 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/litActionHandler.js @@ -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}` }); + } +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/common/batchGenerateEncryptedKeys.js similarity index 70% rename from packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js rename to packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/common/batchGenerateEncryptedKeys.js index cb8df59981..e7d8db8c41 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js +++ b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/common/batchGenerateEncryptedKeys.js @@ -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; @@ -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; @@ -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}`); @@ -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, + }); +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/common/exportPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/common/exportPrivateKey.js new file mode 100644 index 0000000000..1d85015bd3 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/common/exportPrivateKey.js @@ -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 } - Returns a decrypted private key. + */ + +export async function exportPrivateKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, +}) { + return getDecryptedKeyToSingleNode({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + }); +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/ethereum/generateEncryptedEthereumPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/ethereum/generateEncryptedEthereumPrivateKey.js new file mode 100644 index 0000000000..f11cd6c1b1 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/ethereum/generateEncryptedEthereumPrivateKey.js @@ -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, + }); +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/ethereum/signMessageWithEncryptedEthereumKey.js b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/ethereum/signMessageWithEncryptedEthereumKey.js new file mode 100644 index 0000000000..4f651b1072 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/ethereum/signMessageWithEncryptedEthereumKey.js @@ -0,0 +1,36 @@ +const { + getDecryptedKeyToSingleNode, +} = require('../../internal/common/getDecryptedKeyToSingleNode'); +const { + signMessageEthereumKey, +} = require('../../internal/ethereum/signMessage'); + +/** + * Signs a message with the Ethers wallet which is also decrypted inside the Lit Action. + * + * @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 messageToSign - The unsigned message to be signed by the Wrapped Key + * @jsParam accessControlConditions - The access control condition that allows only the pkpAddress to decrypt the Wrapped Key + * + * @returns { Promise } - Returns a message signed by the Ethers Wrapped key. Or returns errors if any. + */ + +export async function signMessageWithEncryptedEthereumKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + messageToSign, +}) { + const privateKey = await getDecryptedKeyToSingleNode({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + }); + + return signMessageEthereumKey({ + privateKey, + messageToSign, + }); +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/ethereum/signTransactionWithEncryptedEthereumKey.js similarity index 50% rename from packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js rename to packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/ethereum/signTransactionWithEncryptedEthereumKey.js index 2f35f19583..81616575f5 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/ethereum/signTransactionWithEncryptedEthereumKey.js @@ -1,13 +1,10 @@ +const { + getDecryptedKeyToSingleNode, +} = require('../../internal/common/getDecryptedKeyToSingleNode'); const { signTransactionEthereumKey, getValidatedUnsignedTx, -} = require('./internal/signTransaction'); -const { - getDecryptedKeyToSingleNode, -} = require('../common/internal/getDecryptedKeyToSingleNode'); -const { removeSaltFromDecryptedKey } = require('../utils'); - -/* global accessControlConditions, ciphertext, dataToEncryptHash, unsignedTransaction, broadcast, Lit */ +} = require('../../internal/ethereum/signTransaction'); /** * @@ -22,34 +19,25 @@ const { removeSaltFromDecryptedKey } = require('../utils'); * * @returns { Promise } - Returns the transaction hash if broadcast is set as true else returns only the signed transaction. Or returns errors if any. */ -(async () => { - try { - const validatedTx = getValidatedUnsignedTx(unsignedTransaction); - - const decryptedPrivateKey = await getDecryptedKeyToSingleNode({ - accessControlConditions, - ciphertext, - dataToEncryptHash, - }); - - if (!decryptedPrivateKey) { - // Silently exit on nodes which didn't run the `decryptToSingleNode` code - return; - } - - const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); +export async function signTransactionWithEncryptedEthereumKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + unsignedTransaction, + broadcast, +}) { + const validatedTx = getValidatedUnsignedTx(unsignedTransaction); - const txResult = await signTransactionEthereumKey({ - broadcast, - privateKey, - unsignedTransaction, - validatedTx, - }); + const privateKey = await getDecryptedKeyToSingleNode({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + }); - Lit.Actions.setResponse({ response: txResult }); - } catch (err) { - Lit.Actions.setResponse({ - response: `Error: ${err.message}`, - }); - } -})(); + return signTransactionEthereumKey({ + broadcast, + privateKey, + unsignedTransaction, + validatedTx, + }); +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/index.js b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/index.js new file mode 100644 index 0000000000..f5c88f070d --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/index.js @@ -0,0 +1,33 @@ +const { + batchGenerateEncryptedKeys, +} = require('./common/batchGenerateEncryptedKeys'); +const { exportPrivateKey } = require('./common/exportPrivateKey'); +const { + generateEncryptedEthereumPrivateKey, +} = require('./ethereum/generateEncryptedEthereumPrivateKey'); +const { + signMessageWithEncryptedEthereumKey, +} = require('./ethereum/signMessageWithEncryptedEthereumKey'); +const { + signTransactionWithEncryptedEthereumKey, +} = require('./ethereum/signTransactionWithEncryptedEthereumKey'); +const { + generateEncryptedSolanaPrivateKey, +} = require('./solana/generateEncryptedSolanaPrivateKey'); +const { + signMessageWithEncryptedSolanaKey, +} = require('./solana/signMessageWithEncryptedSolanaKey'); +const { + signTransactionWithEncryptedSolanaKey, +} = require('./solana/signTransactionWithEncryptedSolanaKey'); + +export const rawActionFunctions = { + exportPrivateKey, + batchGenerateEncryptedKeys, + generateEncryptedEthereumPrivateKey, + signMessageWithEncryptedEthereumKey, + signTransactionWithEncryptedEthereumKey, + generateEncryptedSolanaPrivateKey, + signMessageWithEncryptedSolanaKey, + signTransactionWithEncryptedSolanaKey, +}; diff --git a/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/solana/generateEncryptedSolanaPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/solana/generateEncryptedSolanaPrivateKey.js new file mode 100644 index 0000000000..02277722d9 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/solana/generateEncryptedSolanaPrivateKey.js @@ -0,0 +1,24 @@ +const { encryptPrivateKey } = require('../../internal/common/encryptKey'); +const { + generateSolanaPrivateKey, +} = require('../../internal/solana/generatePrivateKey'); + +/** + * Bundles solana/web3.js package as it's required to generate a random Solana 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 JSON object with ciphertext & dataToEncryptHash which are the result of the encryption. Also returns the publicKey of the newly generated Solana Wrapped Key. + */ +export async function generateEncryptedSolanaPrivateKey({ + accessControlConditions, +}) { + const { privateKey, publicKey } = generateSolanaPrivateKey(); + + return encryptPrivateKey({ + accessControlConditions, + publicKey, + privateKey, + }); +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/solana/signMessageWithEncryptedSolanaKey.js b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/solana/signMessageWithEncryptedSolanaKey.js new file mode 100644 index 0000000000..ec9ade6448 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/solana/signMessageWithEncryptedSolanaKey.js @@ -0,0 +1,35 @@ +const { + getDecryptedKeyToSingleNode, +} = require('../../internal/common/getDecryptedKeyToSingleNode'); +const { signMessageSolanaKey } = require('../../internal/solana/signMessage'); + +/** + * + * Bundles solana/web3.js package as it's required to sign a message with the Solana wallet which is also decrypted inside the Lit Action. + * + * @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 messageToSign - The unsigned message to be signed by the Wrapped Key + * @jsParam accessControlConditions - The access control condition that allows only the pkpAddress to decrypt the Wrapped Key + * + * @returns { Promise } - Returns a message signed by the Solana Wrapped key. Or returns errors if any. + */ + +export async function signMessageWithEncryptedSolanaKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + messageToSign, +}) { + const privateKey = await getDecryptedKeyToSingleNode({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + }); + + return signMessageSolanaKey({ + messageToSign, + privateKey, + }); +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/solana/signTransactionWithEncryptedSolanaKey.js b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/solana/signTransactionWithEncryptedSolanaKey.js new file mode 100644 index 0000000000..d737ec1315 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/solana/signTransactionWithEncryptedSolanaKey.js @@ -0,0 +1,43 @@ +const { + getDecryptedKeyToSingleNode, +} = require('../../internal/common/getDecryptedKeyToSingleNode'); +const { + signTransactionSolanaKey, + validateUnsignedTransaction, +} = require('../../internal/solana/signTransaction'); + +/** + * + * Bundles solana/web3.js package as it's required to sign a transaction with the Solana wallet which is also decrypted inside the Lit Action. + * + * @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 unsignedTransaction - The unsigned message to be signed by the Wrapped Key + * @jsParam broadcast - Flag used to determine whether to just sign the message or also to broadcast it using the node's RPC. + * @jsParam accessControlConditions - The access control condition that allows only the pkpAddress to decrypt the Wrapped Key + * + * @returns { Promise } - Returns the transaction signature. Or returns errors if any. + */ + +export async function signTransactionWithEncryptedSolanaKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + unsignedTransaction, + broadcast, +}) { + validateUnsignedTransaction(unsignedTransaction); + + const privateKey = await getDecryptedKeyToSingleNode({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + }); + + return signTransactionSolanaKey({ + broadcast, + privateKey, + unsignedTransaction, + }); +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/common/batchGenerateEncryptedKeys.js b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/common/batchGenerateEncryptedKeys.js new file mode 100644 index 0000000000..1e658a0d5c --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/common/batchGenerateEncryptedKeys.js @@ -0,0 +1,9 @@ +import { litActionHandler } from '../../litActionHandler'; +import { batchGenerateEncryptedKeys } from '../../raw-action-functions/common/batchGenerateEncryptedKeys'; + +/* global actions accessControlConditions */ + +(async () => + litActionHandler(async () => + batchGenerateEncryptedKeys({ actions, accessControlConditions }) + ))(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/common/exportPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/common/exportPrivateKey.js new file mode 100644 index 0000000000..1b2ecec252 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/common/exportPrivateKey.js @@ -0,0 +1,13 @@ +/* global accessControlConditions, ciphertext, dataToEncryptHash */ + +import { litActionHandler } from '../../litActionHandler'; +import { exportPrivateKey } from '../../raw-action-functions/common/exportPrivateKey'; + +(async () => + litActionHandler(async () => + exportPrivateKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + }) + ))(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/ethereum/generateEncryptedEthereumPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/ethereum/generateEncryptedEthereumPrivateKey.js new file mode 100644 index 0000000000..c79eba28c2 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/ethereum/generateEncryptedEthereumPrivateKey.js @@ -0,0 +1,11 @@ +/* global accessControlConditions */ + +import { litActionHandler } from '../../litActionHandler'; +import { generateEncryptedEthereumPrivateKey } from '../../raw-action-functions/ethereum/generateEncryptedEthereumPrivateKey'; + +(async () => + litActionHandler(async () => + generateEncryptedEthereumPrivateKey({ + accessControlConditions, + }) + ))(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/ethereum/signMessageWithEncryptedEthereumKey.js b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/ethereum/signMessageWithEncryptedEthereumKey.js new file mode 100644 index 0000000000..5ad3e679d1 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/ethereum/signMessageWithEncryptedEthereumKey.js @@ -0,0 +1,14 @@ +/* global accessControlConditions, ciphertext, dataToEncryptHash, messageToSign */ + +import { litActionHandler } from '../../litActionHandler'; +import { signMessageWithEncryptedEthereumKey } from '../../raw-action-functions/ethereum/signMessageWithEncryptedEthereumKey'; + +(async () => + litActionHandler(async () => + signMessageWithEncryptedEthereumKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + messageToSign, + }) + ))(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/ethereum/signTransactionWithEncryptedEthereumKey.js b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/ethereum/signTransactionWithEncryptedEthereumKey.js new file mode 100644 index 0000000000..3c83c0fb35 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/ethereum/signTransactionWithEncryptedEthereumKey.js @@ -0,0 +1,15 @@ +/* global accessControlConditions, ciphertext, dataToEncryptHash, unsignedTransaction, broadcast */ + +import { litActionHandler } from '../../litActionHandler'; +import { signTransactionWithEncryptedEthereumKey } from '../../raw-action-functions/ethereum/signTransactionWithEncryptedEthereumKey'; + +(async () => + litActionHandler(async () => + signTransactionWithEncryptedEthereumKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + unsignedTransaction, + broadcast, + }) + ))(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/solana/generateEncryptedSolanaPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/solana/generateEncryptedSolanaPrivateKey.js new file mode 100644 index 0000000000..2382514531 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/solana/generateEncryptedSolanaPrivateKey.js @@ -0,0 +1,11 @@ +/* global accessControlConditions */ + +import { litActionHandler } from '../../litActionHandler'; +import { generateEncryptedSolanaPrivateKey } from '../../raw-action-functions/solana/generateEncryptedSolanaPrivateKey'; + +(async () => + litActionHandler(async () => + generateEncryptedSolanaPrivateKey({ + accessControlConditions, + }) + ))(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/solana/signMessageWithEncryptedSolanaKey.js b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/solana/signMessageWithEncryptedSolanaKey.js new file mode 100644 index 0000000000..2e3603ba67 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/solana/signMessageWithEncryptedSolanaKey.js @@ -0,0 +1,14 @@ +/* global accessControlConditions, ciphertext, dataToEncryptHash, messageToSign */ + +import { litActionHandler } from '../../litActionHandler'; +import { signMessageWithEncryptedSolanaKey } from '../../raw-action-functions/solana/signMessageWithEncryptedSolanaKey'; + +(async () => + litActionHandler(async () => + signMessageWithEncryptedSolanaKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + messageToSign, + }) + ))(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/solana/signTransactionWithEncryptedSolanaKey.js b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/solana/signTransactionWithEncryptedSolanaKey.js new file mode 100644 index 0000000000..236b930be2 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/solana/signTransactionWithEncryptedSolanaKey.js @@ -0,0 +1,15 @@ +/* global accessControlConditions, ciphertext, dataToEncryptHash, unsignedTransaction, broadcast */ + +import { litActionHandler } from '../../litActionHandler'; +import { signTransactionWithEncryptedSolanaKey } from '../../raw-action-functions/solana/signTransactionWithEncryptedSolanaKey'; + +(async () => + litActionHandler(async () => + signTransactionWithEncryptedSolanaKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + unsignedTransaction, + broadcast, + }) + ))(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js deleted file mode 100644 index 613dd373d3..0000000000 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js +++ /dev/null @@ -1,25 +0,0 @@ -const { generateSolanaPrivateKey } = require('./internal/generatePrivateKey'); -const { encryptPrivateKey } = require('../common/internal/encryptKey'); - -/* global accessControlConditions, Lit */ - -/** - * Bundles solana/web3.js package as it's required to generate a random Solana 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 } - Returns a stringified JSON object with ciphertext & dataToEncryptHash which are the result of the encryption. Also returns the publicKey of the newly generated Solana Wrapped Key. - */ -(async () => { - const { privateKey, publicKey } = generateSolanaPrivateKey(); - const encryptedKeyResult = await encryptPrivateKey({ - accessControlConditions, - publicKey, - privateKey, - }); - - Lit.Actions.setResponse({ - response: JSON.stringify(encryptedKeyResult), - }); -})(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js deleted file mode 100644 index 776b1fafcb..0000000000 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js +++ /dev/null @@ -1,46 +0,0 @@ -const { signMessageSolanaKey } = require('./internal/signMessage'); -const { - getDecryptedKeyToSingleNode, -} = require('../common/internal/getDecryptedKeyToSingleNode'); -const { removeSaltFromDecryptedKey } = require('../utils'); - -/* global accessControlConditions, ciphertext, dataToEncryptHash, messageToSign, Lit */ - -/** - * - * Bundles solana/web3.js package as it's required to sign a message with the Solana wallet which is also decrypted inside the Lit Action. - * - * @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 messageToSign - The unsigned message to be signed by the Wrapped Key - * @jsParam accessControlConditions - The access control condition that allows only the pkpAddress to decrypt the Wrapped Key - * - * @returns { Promise } - Returns a message signed by the Solana Wrapped key. Or returns errors if any. - */ - -(async () => { - try { - const decryptedPrivateKey = await getDecryptedKeyToSingleNode({ - accessControlConditions, - ciphertext, - dataToEncryptHash, - }); - - if (!decryptedPrivateKey) { - // Silently exit on nodes which didn't run the `decryptToSingleNode` code - return; - } - - const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); - - const signature = await signMessageSolanaKey({ - messageToSign, - privateKey, - }); - - Lit.Actions.setResponse({ response: signature }); - } catch (err) { - Lit.Actions.setResponse({ response: `Error: ${err.message}` }); - } -})(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js deleted file mode 100644 index 5a243e9c42..0000000000 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js +++ /dev/null @@ -1,55 +0,0 @@ -const { - signTransactionSolanaKey, - validateUnsignedTransaction, -} = require('./internal/signTransaction'); -const { - getDecryptedKeyToSingleNode, -} = require('../common/internal/getDecryptedKeyToSingleNode'); -const { removeSaltFromDecryptedKey } = require('../utils'); - -/* global accessControlConditions, ciphertext, dataToEncryptHash, unsignedTransaction, Lit, broadcast */ - -/** - * - * Bundles solana/web3.js package as it's required to sign a transaction with the Solana wallet which is also decrypted inside the Lit Action. - * - * @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 unsignedTransaction - The unsigned message to be signed by the Wrapped Key - * @jsParam broadcast - Flag used to determine whether to just sign the message or also to broadcast it using the node's RPC. - * @jsParam accessControlConditions - The access control condition that allows only the pkpAddress to decrypt the Wrapped Key - * - * @returns { Promise } - Returns the transaction signature. Or returns errors if any. - */ - -(async () => { - try { - validateUnsignedTransaction(unsignedTransaction); - - const decryptedPrivateKey = await getDecryptedKeyToSingleNode({ - accessControlConditions, - ciphertext, - dataToEncryptHash, - }); - - if (!decryptedPrivateKey) { - // Silently exit on nodes which didn't run the `decryptToSingleNode` code - return; - } - - const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); - - const txResult = await signTransactionSolanaKey({ - broadcast, - privateKey, - unsignedTransaction, - }); - - Lit.Actions.setResponse({ response: txResult }); - } catch (err) { - Lit.Actions.setResponse({ - response: `Error: ${err.message}`, - }); - } -})(); diff --git a/packages/wrapped-keys-lit-actions/tsconfig.json b/packages/wrapped-keys-lit-actions/tsconfig.json index d87cb2e661..dd441376c9 100644 --- a/packages/wrapped-keys-lit-actions/tsconfig.json +++ b/packages/wrapped-keys-lit-actions/tsconfig.json @@ -8,7 +8,8 @@ "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "allowJs": true + "allowJs": true, + "checkJs": false }, "files": [], "include": [], diff --git a/packages/wrapped-keys-lit-actions/tsconfig.lib.json b/packages/wrapped-keys-lit-actions/tsconfig.lib.json index d2836b051e..8261486edc 100644 --- a/packages/wrapped-keys-lit-actions/tsconfig.lib.json +++ b/packages/wrapped-keys-lit-actions/tsconfig.lib.json @@ -5,7 +5,7 @@ "declaration": true, "types": [], "allowJs": true, - "checkJs": true + "checkJs": false }, "include": ["**/*.ts"], "exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts"] diff --git a/packages/wrapped-keys-lit-actions/tsconfig.spec.json b/packages/wrapped-keys-lit-actions/tsconfig.spec.json index a2f7dd30d7..48d6d00bb4 100644 --- a/packages/wrapped-keys-lit-actions/tsconfig.spec.json +++ b/packages/wrapped-keys-lit-actions/tsconfig.spec.json @@ -4,7 +4,8 @@ "outDir": "../../dist/out-tsc", "module": "commonjs", "types": ["jest", "node"], - "allowJs": true + "allowJs": true, + "checkJs": false }, "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] }