From 0b40f472693eabbeedcc161a7c20f44398d27f87 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Tue, 1 Oct 2024 20:18:43 +0100 Subject: [PATCH 01/53] refactor(wrapped-keys-lit-actions): LIT-3920 - Deconstruct existing Ethereum wrapped keys LIT actions into composable chunks. Rename LIT action files for consistency. --- .../esbuild.config.js | 4 +- .../wrapped-keys-lit-actions/src/index.ts | 4 +- .../lib/common/internal/getDecryptedKey.js | 22 ++++++ .../src/lib/constants.js | 1 + .../generateEncryptedEthereumPrivateKey.js | 31 +++------ .../internal/generateEncryptedPrivateKey.js | 29 ++++++++ .../internal/signMessageWithEncryptedKey.js | 48 +++++++++++++ .../signMessageWithEncryptedEthereumKey.js | 32 +++++++++ .../signMessageWithEthereumEncryptedKey.js | 67 ------------------- ...ignTransactionWithEncryptedEthereumKey.js} | 0 .../wrapped-keys-lit-actions/src/lib/utils.js | 4 +- 11 files changed, 147 insertions(+), 95 deletions(-) create mode 100644 packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKey.js create mode 100644 packages/wrapped-keys-lit-actions/src/lib/constants.js create mode 100644 packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generateEncryptedPrivateKey.js create mode 100644 packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js create mode 100644 packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js delete mode 100644 packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEthereumEncryptedKey.js rename packages/wrapped-keys-lit-actions/src/lib/ethereum/{signTransactionWithEthereumEncryptedKey.js => signTransactionWithEncryptedEthereumKey.js} (100%) diff --git a/packages/wrapped-keys-lit-actions/esbuild.config.js b/packages/wrapped-keys-lit-actions/esbuild.config.js index 908ea24636..9b54f0cc26 100644 --- a/packages/wrapped-keys-lit-actions/esbuild.config.js +++ b/packages/wrapped-keys-lit-actions/esbuild.config.js @@ -53,8 +53,8 @@ module.exports = { './src/lib/solana/signTransactionWithSolanaEncryptedKey.js', './src/lib/solana/signMessageWithSolanaEncryptedKey.js', './src/lib/solana/generateEncryptedSolanaPrivateKey.js', - './src/lib/ethereum/signTransactionWithEthereumEncryptedKey.js', - './src/lib/ethereum/signMessageWithEthereumEncryptedKey.js', + './src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js', + './src/lib/ethereum/signMessageWithEncryptedEthereumKey.js', './src/lib/ethereum/generateEncryptedEthereumPrivateKey.js', './src/lib/common/exportPrivateKey.js', ], diff --git a/packages/wrapped-keys-lit-actions/src/index.ts b/packages/wrapped-keys-lit-actions/src/index.ts index 1b4f53103b..54d090ae7e 100644 --- a/packages/wrapped-keys-lit-actions/src/index.ts +++ b/packages/wrapped-keys-lit-actions/src/index.ts @@ -1,7 +1,7 @@ import * as exportPrivateKey from './generated/common/exportPrivateKey'; import * as generateEncryptedEthereumPrivateKey from './generated/ethereum/generateEncryptedEthereumPrivateKey'; -import * as signMessageWithEthereumEncryptedKey from './generated/ethereum/signMessageWithEthereumEncryptedKey'; -import * as signTransactionWithEthereumEncryptedKey from './generated/ethereum/signTransactionWithEthereumEncryptedKey'; +import * as signMessageWithEthereumEncryptedKey from './generated/ethereum/signMessageWithEncryptedEthereumKey'; +import * as signTransactionWithEthereumEncryptedKey from './generated/ethereum/signTransactionWithEncryptedEthereumKey'; import * as generateEncryptedSolanaPrivateKey from './generated/solana/generateEncryptedSolanaPrivateKey'; import * as signMessageWithSolanaEncryptedKey from './generated/solana/signMessageWithSolanaEncryptedKey'; import * as signTransactionWithSolanaEncryptedKey from './generated/solana/signTransactionWithSolanaEncryptedKey'; diff --git a/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKey.js new file mode 100644 index 0000000000..1484ab5ea0 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKey.js @@ -0,0 +1,22 @@ +/* global Lit */ + +export async function getDecryptedKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, +}) { + try { + // May be undefined, since we're using `decryptToSingleNode` + const privateKey = await Lit.Actions.decryptToSingleNode({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + chain: 'ethereum', + authSig: null, + }); + + return privateKey; + } catch (err) { + throw new Error('When decrypting key to a single node - ' + err.message); + } +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/constants.js b/packages/wrapped-keys-lit-actions/src/lib/constants.js new file mode 100644 index 0000000000..3e3434d67b --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/constants.js @@ -0,0 +1 @@ +export const LIT_PREFIX = 'lit_'; diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js index 729b4f8913..bf5e9aa5ea 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js @@ -1,37 +1,24 @@ +/* global accessControlConditions, Lit */ + /** * - * Generates a random Ethers private key and only allows the provided PKP to to decrypt it + * 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/generateEncryptedPrivateKey'; (async () => { - const LIT_PREFIX = 'lit_'; - - const resp = await Lit.Actions.runOnce( - { waitForResponse: true, name: 'encryptedPrivateKey' }, - async () => { - const wallet = ethers.Wallet.createRandom(); - const privateKey = LIT_PREFIX + wallet.privateKey.toString(); - let utf8Encode = new TextEncoder(); - const to_encrypt = utf8Encode.encode(privateKey); - - const { ciphertext, dataToEncryptHash } = await Lit.Actions.encrypt({ - accessControlConditions, - to_encrypt, - }); - return JSON.stringify({ - ciphertext, - dataToEncryptHash, - publicKey: wallet.publicKey, - }); - } + const generatedKeyResultStr = await Lit.Actions.runOnce( + { waitForResponse: true, name: 'generateEthereumPrivateKey' }, + async () => + JSON.stringify(generateEthereumPrivateKey({ accessControlConditions })) ); Lit.Actions.setResponse({ - response: resp, + response: generatedKeyResultStr, }); })(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generateEncryptedPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generateEncryptedPrivateKey.js new file mode 100644 index 0000000000..10f437d297 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generateEncryptedPrivateKey.js @@ -0,0 +1,29 @@ +/* global ethers, Lit */ + +/** + * Generates a random Ethers private key that only allows the provided PKP to decrypt it + * This should be executed using `runOnce` to avoid generating `n` new private keys where we only want 1. + * + * @private + * @returns { Promise<{ciphertext: string, dataToEncryptHash: string, publicKey: string}> } - The ciphertext & dataToEncryptHash which are the result of the encryption, and the publicKey of the newly generated Ethers Wrapped Key. + */ +import { LIT_PREFIX } from '../../constants'; + +export async function generateEthereumPrivateKey({ accessControlConditions }) { + const wallet = ethers.Wallet.createRandom(); + const privateKey = LIT_PREFIX + wallet.privateKey.toString(); + + let utf8Encode = new TextEncoder(); + const to_encrypt = utf8Encode.encode(privateKey); + + const { ciphertext, dataToEncryptHash } = await Lit.Actions.encrypt({ + accessControlConditions, + to_encrypt, + }); + + return { + ciphertext, + dataToEncryptHash, + publicKey: wallet.publicKey, + }; +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js new file mode 100644 index 0000000000..18406ecc1e --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js @@ -0,0 +1,48 @@ +/* global ethers */ + +import { getDecryptedKey } from '../../common/internal/getDecryptedKey'; +import { removeSaltFromDecryptedKey } from '../../utils'; + +async function trySignMessage({ privateKey, messageToSign }) { + try { + const wallet = new ethers.Wallet(privateKey); + const signature = await wallet.signMessage(messageToSign); + + return { signature, walletAddress: wallet.address }; + } catch (err) { + throw new Error('When signing message - ' + err.message); + } +} + +export async function signMessageWithEncryptedKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + messageToSign, +}) { + const decryptedPrivateKey = await getDecryptedKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + }); + + if (!decryptedPrivateKey) { + // Silently exit on nodes which didn't run the `decryptToSingleNode` code + return; + } + + const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); + + const { signature, walletAddress } = await trySignMessage({ + privateKey, + messageToSign, + }); + + const recoveredAddress = ethers.utils.verifyMessage(messageToSign, signature); + + if (recoveredAddress !== walletAddress) { + throw new Error("Recovered address doesn't match the wallet address"); + } + + return { signature, walletAddress }; +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js new file mode 100644 index 0000000000..b385200b2a --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js @@ -0,0 +1,32 @@ +/* global accessControlConditions, ciphertext, dataToEncryptHash, messageToSign, Lit */ + +const { + signMessageWithEncryptedKey, +} = require('./internal/signMessageWithEncryptedKey'); + +/** + * 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 { signature } = await signMessageWithEncryptedKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + 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/ethereum/signMessageWithEthereumEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEthereumEncryptedKey.js deleted file mode 100644 index 25e691906b..0000000000 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEthereumEncryptedKey.js +++ /dev/null @@ -1,67 +0,0 @@ -const { removeSaltFromDecryptedKey } = require('../utils'); - -/** - * - * 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 () => { - let decryptedPrivateKey; - try { - decryptedPrivateKey = await Lit.Actions.decryptToSingleNode({ - accessControlConditions, - ciphertext, - dataToEncryptHash, - chain: 'ethereum', - authSig: null, - }); - } catch (err) { - const errorMessage = - 'Error: When decrypting to a single node- ' + err.message; - Lit.Actions.setResponse({ response: errorMessage }); - return; - } - - if (!decryptedPrivateKey) { - // Exit the nodes which don't have the decryptedData - return; - } - - let privateKey; - try { - privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); - } catch (err) { - Lit.Actions.setResponse({ response: err.message }); - return; - } - const wallet = new ethers.Wallet(privateKey); - - try { - const signature = await wallet.signMessage(messageToSign); - - const recoveredAddress = ethers.utils.verifyMessage( - messageToSign, - signature - ); - - if (recoveredAddress !== wallet.address) { - Lit.Actions.setResponse({ - response: "Error: Recovered address doesn't match the wallet address", - }); - return; - } - - Lit.Actions.setResponse({ response: signature }); - } catch (err) { - const errorMessage = 'Error: When signing message- ' + err.message; - Lit.Actions.setResponse({ response: errorMessage }); - } -})(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEthereumEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js similarity index 100% rename from packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEthereumEncryptedKey.js rename to packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js diff --git a/packages/wrapped-keys-lit-actions/src/lib/utils.js b/packages/wrapped-keys-lit-actions/src/lib/utils.js index 34c7e9be25..4cfc44eecf 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/utils.js +++ b/packages/wrapped-keys-lit-actions/src/lib/utils.js @@ -1,9 +1,9 @@ -const LIT_PREFIX = 'lit_'; +import { LIT_PREFIX } from './constants'; export function removeSaltFromDecryptedKey(decryptedPrivateKey) { if (!decryptedPrivateKey.startsWith(LIT_PREFIX)) { throw new Error( - `Error: PKey was not encrypted with salt; all wrapped keys must be prefixed with '${LIT_PREFIX}'` + `PKey was not encrypted with salt; all wrapped keys must be prefixed with '${LIT_PREFIX}'` ); } From b939e6d97684253dd2e864d09ac957f4d27d8af0 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Tue, 1 Oct 2024 21:34:48 +0100 Subject: [PATCH 02/53] refactor(wrapped-keys-lit-actions): LIT-3920 - Deconstruct existing Solana wrapped keys LIT actions into composable chunks. Rename LIT action files for consistency, and standardize structure & error handling with `signMessageWithEncryptedKey` for ethereum --- .../esbuild.config.js | 4 +- .../wrapped-keys-lit-actions/src/index.ts | 5 +- .../internal/signMessageWithEncryptedKey.js | 22 ++++- .../generateEncryptedSolanaPrivateKey.js | 37 +++----- .../internal/generateEncryptedPrivateKey.js | 31 +++++++ .../internal/signMessageWithEncryptedKey.js | 68 +++++++++++++++ .../signMessageWithEncryptedSolanaKey.js | 33 +++++++ .../signMessageWithSolanaEncryptedKey.js | 85 ------------------- ... signTransactionWithEncryptedSolanaKey.js} | 0 9 files changed, 166 insertions(+), 119 deletions(-) create mode 100644 packages/wrapped-keys-lit-actions/src/lib/solana/internal/generateEncryptedPrivateKey.js create mode 100644 packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessageWithEncryptedKey.js create mode 100644 packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js delete mode 100644 packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithSolanaEncryptedKey.js rename packages/wrapped-keys-lit-actions/src/lib/solana/{signTransactionWithSolanaEncryptedKey.js => signTransactionWithEncryptedSolanaKey.js} (100%) diff --git a/packages/wrapped-keys-lit-actions/esbuild.config.js b/packages/wrapped-keys-lit-actions/esbuild.config.js index 9b54f0cc26..6741edee93 100644 --- a/packages/wrapped-keys-lit-actions/esbuild.config.js +++ b/packages/wrapped-keys-lit-actions/esbuild.config.js @@ -50,8 +50,8 @@ module.exports = { (async () => { await esbuild.build({ entryPoints: [ - './src/lib/solana/signTransactionWithSolanaEncryptedKey.js', - './src/lib/solana/signMessageWithSolanaEncryptedKey.js', + './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', diff --git a/packages/wrapped-keys-lit-actions/src/index.ts b/packages/wrapped-keys-lit-actions/src/index.ts index 54d090ae7e..f1960f5882 100644 --- a/packages/wrapped-keys-lit-actions/src/index.ts +++ b/packages/wrapped-keys-lit-actions/src/index.ts @@ -3,8 +3,8 @@ import * as generateEncryptedEthereumPrivateKey from './generated/ethereum/gener import * as signMessageWithEthereumEncryptedKey from './generated/ethereum/signMessageWithEncryptedEthereumKey'; import * as signTransactionWithEthereumEncryptedKey from './generated/ethereum/signTransactionWithEncryptedEthereumKey'; import * as generateEncryptedSolanaPrivateKey from './generated/solana/generateEncryptedSolanaPrivateKey'; -import * as signMessageWithSolanaEncryptedKey from './generated/solana/signMessageWithSolanaEncryptedKey'; -import * as signTransactionWithSolanaEncryptedKey from './generated/solana/signTransactionWithSolanaEncryptedKey'; +import * as signMessageWithSolanaEncryptedKey from './generated/solana/signMessageWithEncryptedSolanaKey'; +import * as signTransactionWithSolanaEncryptedKey from './generated/solana/signTransactionWithEncryptedSolanaKey'; import type { LitActionCodeRepository } from '@lit-protocol/wrapped-keys'; @@ -36,6 +36,7 @@ export { generateEncryptedSolanaPrivateKey, signMessageWithSolanaEncryptedKey, signTransactionWithSolanaEncryptedKey, + // Full export to bundle all lit actions litActionRepository, }; diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js index 18406ecc1e..8bbda9d857 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js @@ -3,7 +3,7 @@ import { getDecryptedKey } from '../../common/internal/getDecryptedKey'; import { removeSaltFromDecryptedKey } from '../../utils'; -async function trySignMessage({ privateKey, messageToSign }) { +async function signMessage({ privateKey, messageToSign }) { try { const wallet = new ethers.Wallet(privateKey); const signature = await wallet.signMessage(messageToSign); @@ -14,6 +14,14 @@ async function trySignMessage({ privateKey, messageToSign }) { } } +function verifyMessageSignature(messageToSign, signature) { + try { + return ethers.utils.verifyMessage(messageToSign, signature); + } catch (err) { + throw new Error(`When validating signed message is valid: ${err.message}`); + } +} + export async function signMessageWithEncryptedKey({ accessControlConditions, ciphertext, @@ -33,15 +41,21 @@ export async function signMessageWithEncryptedKey({ const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); - const { signature, walletAddress } = await trySignMessage({ + const { signature, walletAddress } = await signMessage({ privateKey, messageToSign, }); - const recoveredAddress = ethers.utils.verifyMessage(messageToSign, signature); + const recoveredAddress = verifyMessageSignature( + messageToSign, + signature, + walletAddress + ); if (recoveredAddress !== walletAddress) { - throw new Error("Recovered address doesn't match the wallet address"); + throw new Error( + "Recovered address from verifyMessage doesn't match the wallet address" + ); } return { signature, walletAddress }; diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js index 4d757f748c..5884c8dba8 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js @@ -1,40 +1,25 @@ +const { + generateSolanaPrivateKey, +} = require('./internal/generateEncryptedPrivateKey'); + +/* 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 to decrypt it + * 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. */ - -const { Keypair } = require('@solana/web3.js'); - (async () => { - const LIT_PREFIX = 'lit_'; - - const resp = await Lit.Actions.runOnce( - { waitForResponse: true, name: 'encryptedPrivateKey' }, - async () => { - const solanaKeypair = Keypair.generate(); - const privateKey = - LIT_PREFIX + Buffer.from(solanaKeypair.secretKey).toString('hex'); - let utf8Encode = new TextEncoder(); - const to_encrypt = utf8Encode.encode(privateKey); - - const { ciphertext, dataToEncryptHash } = await Lit.Actions.encrypt({ - accessControlConditions, - to_encrypt, - }); - return JSON.stringify({ - ciphertext, - dataToEncryptHash, - publicKey: solanaKeypair.publicKey.toString(), - }); - } + const generatedKeyResultStr = await Lit.Actions.runOnce( + { waitForResponse: true, name: 'generateSolanaPrivateKey' }, + () => JSON.stringify(generateSolanaPrivateKey({ accessControlConditions })) ); Lit.Actions.setResponse({ - response: resp, + response: generatedKeyResultStr, }); })(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generateEncryptedPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generateEncryptedPrivateKey.js new file mode 100644 index 0000000000..f677dc26dc --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generateEncryptedPrivateKey.js @@ -0,0 +1,31 @@ +import { Keypair } from '@solana/web3.js'; + +import { LIT_PREFIX } from '../../constants'; + +/* global 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 + * This should be executed using `runOnce` to avoid generating `n` new private keys where we only want 1. + * + * @private + * @returns { Promise<{ciphertext: string, dataToEncryptHash: string, publicKey: string}> } - The ciphertext & dataToEncryptHash which are the result of the encryption, and the publicKey of the newly generated Ethers Wrapped Key. + */ +export async function generateSolanaPrivateKey({ accessControlConditions }) { + const solanaKeypair = Keypair.generate(); + const privateKey = + LIT_PREFIX + Buffer.from(solanaKeypair.secretKey).toString('hex'); + let utf8Encode = new TextEncoder(); + const to_encrypt = utf8Encode.encode(privateKey); + + const { ciphertext, dataToEncryptHash } = await Lit.Actions.encrypt({ + accessControlConditions, + to_encrypt, + }); + + return { + ciphertext, + dataToEncryptHash, + publicKey: solanaKeypair.publicKey.toString(), + }; +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessageWithEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessageWithEncryptedKey.js new file mode 100644 index 0000000000..24344f0733 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessageWithEncryptedKey.js @@ -0,0 +1,68 @@ +import { Keypair } from '@solana/web3.js'; +import bs58 from 'bs58'; +import nacl from 'tweetnacl'; + +import { getDecryptedKey } from '../../common/internal/getDecryptedKey'; +import { removeSaltFromDecryptedKey } from '../../utils'; + +async function signMessage({ messageToSign, solanaKeyPair }) { + try { + const signature = nacl.sign.detached( + new TextEncoder().encode(messageToSign), + solanaKeyPair.secretKey + ); + + return { signature }; + } catch (err) { + throw new Error('When signing message - ' + err.message); + } +} + +function verifyMessageSignature({ signature, solanaKeyPair, messageToSign }) { + try { + const isValid = nacl.sign.detached.verify( + Buffer.from(messageToSign), + signature, + solanaKeyPair.publicKey.toBuffer() + ); + + return isValid; + } catch (err) { + throw new Error(`When validating signed message is valid: ${err.message}`); + } +} + +export async function signMessageWithEncryptedKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + messageToSign, +}) { + const decryptedPrivateKey = await getDecryptedKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + }); + + if (!decryptedPrivateKey) { + // Silently exit on nodes which didn't run the `decryptToSingleNode` code + return; + } + + const solanaKeyPair = Keypair.fromSecretKey( + Buffer.from(removeSaltFromDecryptedKey(decryptedPrivateKey), 'hex') + ); + + const { signature } = await signMessage({ + messageToSign, + solanaKeyPair, + }); + + const isValid = verifyMessageSignature({ signature, solanaKeyPair }); + + if (!isValid) { + throw new Error('Signature did not verify to expected Solana public key'); + } + + return { signature: bs58.encode(signature) }; +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js new file mode 100644 index 0000000000..079dd2bd84 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js @@ -0,0 +1,33 @@ +/* global accessControlConditions, ciphertext, dataToEncryptHash, messageToSign, Lit */ + +const { + signMessageWithEncryptedKey, +} = require('./internal/signMessageWithEncryptedKey'); + +/** + * + * 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 { signature } = await signMessageWithEncryptedKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + 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/solana/signMessageWithSolanaEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithSolanaEncryptedKey.js deleted file mode 100644 index 6da17a5233..0000000000 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithSolanaEncryptedKey.js +++ /dev/null @@ -1,85 +0,0 @@ -const { Keypair } = require('@solana/web3.js'); -const bs58 = require('bs58'); -const nacl = require('tweetnacl'); - -const { removeSaltFromDecryptedKey } = require('../utils'); - -/** - * - * 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 () => { - let decryptedPrivateKey; - try { - decryptedPrivateKey = await Lit.Actions.decryptToSingleNode({ - accessControlConditions, - chain: 'ethereum', - ciphertext, - dataToEncryptHash, - authSig: null, - }); - } catch (error) { - Lit.Actions.setResponse({ - response: `Error: When decrypting data to private key: ${error.message}`, - }); - return; - } - - if (!decryptedPrivateKey) { - // Exit the nodes which don't have the decryptedData - return; - } - - let privateKey; - try { - privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); - } catch (err) { - Lit.Actions.setResponse({ response: err.message }); - return; - } - const solanaKeyPair = Keypair.fromSecretKey(Buffer.from(privateKey, 'hex')); - - let signature; - try { - signature = nacl.sign.detached( - new TextEncoder().encode(messageToSign), - solanaKeyPair.secretKey - ); - } catch (error) { - Lit.Actions.setResponse({ - response: `Error: When signing message: ${error.message}`, - }); - return; - } - - try { - const isValid = nacl.sign.detached.verify( - Buffer.from(messageToSign), - signature, - solanaKeyPair.publicKey.toBuffer() - ); - if (!isValid) { - Lit.Actions.setResponse({ - response: - 'Error: Signature did not verify to expected Solana public key', - }); - return; - } - } catch (error) { - Lit.Actions.setResponse({ - response: `Error: When validating signed message is valid: ${error.message}`, - }); - return; - } - - Lit.Actions.setResponse({ response: bs58.encode(signature) }); -})(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithSolanaEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js similarity index 100% rename from packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithSolanaEncryptedKey.js rename to packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js From 72864d2c77f77c1695af7c468750c5df38f5dec0 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Tue, 1 Oct 2024 21:36:43 +0100 Subject: [PATCH 03/53] refactor(wrapped-keys-lit-actions): LIT-3920 - Refactor `common/exportPrivateKey` to use `getDecryptedKey()` and update error logging appropriately --- .../src/lib/common/exportPrivateKey.js | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/packages/wrapped-keys-lit-actions/src/lib/common/exportPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/common/exportPrivateKey.js index ae8d85bd72..01f412d35a 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/common/exportPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/common/exportPrivateKey.js @@ -1,5 +1,8 @@ +const { getDecryptedKey } = require('./internal/getDecryptedKey'); const { removeSaltFromDecryptedKey } = require('../utils'); +/* global accessControlConditions, ciphertext, dataToEncryptHash, Lit */ + /** * * Exports the private key after decrypting and removing the salt from it. @@ -13,31 +16,21 @@ const { removeSaltFromDecryptedKey } = require('../utils'); */ (async () => { - let decryptedPrivateKey; try { - decryptedPrivateKey = await Lit.Actions.decryptToSingleNode({ + const decryptedPrivateKey = await getDecryptedKey({ accessControlConditions, ciphertext, dataToEncryptHash, - chain: 'ethereum', - authSig: null, }); - } catch (err) { - const errorMessage = - 'Error: When decrypting to a single node- ' + err.message; - Lit.Actions.setResponse({ response: errorMessage }); - return; - } - if (!decryptedPrivateKey) { - // Exit the nodes which don't have the decryptedData - return; - } + if (!decryptedPrivateKey) { + // Silently exit on nodes which didn't run the `decryptToSingleNode` code + return; + } - try { const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); Lit.Actions.setResponse({ response: privateKey }); } catch (err) { - Lit.Actions.setResponse({ response: err.message }); + Lit.Actions.setResponse({ response: 'Error: ' + err.message }); } })(); From 7dafaf2c541d6e5dc056775c77e8216f9fea9d21 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Tue, 1 Oct 2024 22:31:12 +0100 Subject: [PATCH 04/53] refactor(wrapped-keys-lit-actions): LIT-3920 - Refactor `signTransactionWithEncryptedEthereumKey` to use composable functions --- .../signTransactionWithEncryptedKey.js | 161 ++++++++++++++++++ ...signTransactionWithEncryptedEthereumKey.js | 136 ++------------- 2 files changed, 172 insertions(+), 125 deletions(-) create mode 100644 packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransactionWithEncryptedKey.js diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransactionWithEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransactionWithEncryptedKey.js new file mode 100644 index 0000000000..175243f116 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransactionWithEncryptedKey.js @@ -0,0 +1,161 @@ +import { getDecryptedKey } from '../../common/internal/getDecryptedKey'; +import { removeSaltFromDecryptedKey } from '../../utils'; + +/* global ethers, Lit */ + +function getValidatedUnsignedTx(unsignedTransaction) { + try { + if (!unsignedTransaction.toAddress) { + throw new Error('Missing required field: toAddress'); + } + + if (!unsignedTransaction.chain) { + throw new Error('Missing required field: chain'); + } + + if (!unsignedTransaction.value) { + throw new Error('Missing required field: value'); + } + + if (!unsignedTransaction.chainId) { + throw new Error('Missing required field: chainId'); + } + + return { + to: unsignedTransaction.toAddress, + value: ethers.utils.hexlify( + ethers.utils.parseEther(unsignedTransaction.value) + ), + chainId: unsignedTransaction.chainId, + data: unsignedTransaction.dataHex, + }; + } catch (err) { + throw new Error('Invalid unsignedTransaction - ' + err.message); + } +} + +async function getLatestNonce({ walletAddress, chain }) { + try { + const nonce = await Lit.Actions.getLatestNonce({ + address: walletAddress, + chain: chain, + }); + + return nonce; + } catch (err) { + throw new Error('Unable to get latest nonce - ' + err.message); + } +} + +async function getEthersRPCProvider({ chain }) { + try { + const rpcUrl = await Lit.Actions.getRpcUrl({ + chain, + }); + + return new ethers.providers.JsonRpcProvider(rpcUrl); + } catch (err) { + throw new Error( + 'Getting the rpc for the chain: ${unsignedTransaction.chain} - ' + + err.message + ); + } +} + +async function getGasPrice({ userProvidedGasPrice, provider }) { + try { + if (userProvidedGasPrice) { + return ethers.utils.parseUnits(userProvidedGasPrice, 'gwei'); + } else { + return await provider.getGasPrice(); + } + } catch (err) { + throw new Error('When getting gas price - ' + err.message); + } +} + +async function getGasLimit({ provider, userProvidedGasLimit, tx }) { + if (userProvidedGasLimit) { + return userProvidedGasLimit; + } else { + try { + return await provider.estimateGas(tx); + } catch (err) { + throw new Error('When estimating gas - ' + err.message); + } + } +} + +async function signTransaction({ tx, wallet }) { + try { + return await wallet.signTransaction(tx); + } catch (err) { + throw new Error('When signing transaction - ' + err.message); + } +} + +async function broadcastTransaction({ provider, signedTx }) { + try { + return await provider.sendTransaction(signedTx); + } catch (err) { + throw new Error('When sending transaction - ' + err.message); + } +} + +export async function signTransactionWithEncryptedKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + unsignedTransaction, + broadcast, +}) { + const tx = getValidatedUnsignedTx(unsignedTransaction); + + const decryptedPrivateKey = await getDecryptedKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + }); + + if (!decryptedPrivateKey) { + // Silently exit on nodes which didn't run the `decryptToSingleNode` code + return; + } + + const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); + const wallet = new ethers.Wallet(privateKey); + + tx.from = wallet.address; + + const [nonce, provider] = await Promise.all([ + getLatestNonce({ + walletAddress: wallet.address, + chain: unsignedTransaction.chain, + }), + getEthersRPCProvider({ + chain: unsignedTransaction.chain, + }), + ]); + + tx.nonce = nonce; + + tx.gasPrice = await getGasPrice({ + provider, + userProvidedGasPrice: unsignedTransaction.gasPrice, + }); + + tx.gasLimit = await getGasLimit({ + provider, + tx, + userProvidedGasLimit: unsignedTransaction.gasLimit, + }); + + const signedTx = await signTransaction({ tx, wallet }); + + if (!broadcast) { + return signedTx; + } + + const txResponse = await broadcastTransaction({ provider, signedTx }); + return txResponse.hash; +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js index 64f6bcf276..b6a0952eaf 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js @@ -1,4 +1,8 @@ -const { removeSaltFromDecryptedKey } = require('../utils'); +const { + signTransactionWithEncryptedKey, +} = require('./internal/signTransactionWithEncryptedKey'); + +/* global accessControlConditions, ciphertext, dataToEncryptHash, unsignedTransaction, broadcast, Lit */ /** * @@ -14,137 +18,19 @@ 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 () => { - if (!unsignedTransaction.toAddress) { - Lit.Actions.setResponse({ - response: 'Error: Missing required field: toAddress', - }); - return; - } - - if (!unsignedTransaction.chain) { - Lit.Actions.setResponse({ - response: 'Error: Missing required field: chain', - }); - return; - } - - if (!unsignedTransaction.value) { - Lit.Actions.setResponse({ - response: 'Error: Missing required field: value', - }); - return; - } - - if (!unsignedTransaction.chainId) { - Lit.Actions.setResponse({ - response: 'Error: Missing required field: chainId', - }); - return; - } - - let decryptedPrivateKey; try { - decryptedPrivateKey = await Lit.Actions.decryptToSingleNode({ + const txResult = await signTransactionWithEncryptedKey({ accessControlConditions, ciphertext, dataToEncryptHash, - chain: 'ethereum', - authSig: null, + unsignedTransaction, + broadcast, }); - } catch (err) { - const errorMessage = - 'Error: When decrypting to a single node- ' + err.message; - Lit.Actions.setResponse({ response: errorMessage }); - return; - } - if (!decryptedPrivateKey) { - // Exit the nodes which don't have the decryptedData - return; - } - - let privateKey; - try { - privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); + Lit.Actions.setResponse({ response: txResult }); } catch (err) { - Lit.Actions.setResponse({ response: err.message }); - return; - } - const wallet = new ethers.Wallet(privateKey); - - let nonce; - try { - nonce = await Lit.Actions.getLatestNonce({ - address: wallet.address, - chain: unsignedTransaction.chain, - }); - } catch (err) { - const errorMessage = 'Error: Unable to get the nonce- ' + err.message; - Lit.Actions.setResponse({ response: errorMessage }); - return; - } - - const tx = { - to: unsignedTransaction.toAddress, - from: wallet.address, - value: ethers.utils.hexlify( - ethers.utils.parseEther(unsignedTransaction.value) - ), - chainId: unsignedTransaction.chainId, - data: unsignedTransaction.dataHex, - nonce, - }; - - let provider; - try { - const rpcUrl = await Lit.Actions.getRpcUrl({ - chain: unsignedTransaction.chain, + Lit.Actions.setResponse({ + response: `Error: ${err.message}`, }); - provider = new ethers.providers.JsonRpcProvider(rpcUrl); - } catch (err) { - const errorMessage = - `Error: Getting the rpc for the chain: ${unsignedTransaction.chain}- ` + - err.message; - Lit.Actions.setResponse({ response: errorMessage }); - return; - } - - if (unsignedTransaction.gasPrice) { - tx.gasPrice = ethers.utils.parseUnits(unsignedTransaction.gasPrice, 'gwei'); - } else { - try { - tx.gasPrice = await provider.getGasPrice(); - } catch (err) { - const errorMessage = 'Error: When getting gas price- ' + err.message; - Lit.Actions.setResponse({ response: errorMessage }); - return; - } - } - - if (unsignedTransaction.gasLimit) { - tx.gasLimit = unsignedTransaction.gasLimit; - } else { - try { - tx.gasLimit = await provider.estimateGas(tx); - } catch (err) { - const errorMessage = 'Error: When estimating gas- ' + err.message; - Lit.Actions.setResponse({ response: errorMessage }); - return; - } - } - - try { - const signedTx = await wallet.signTransaction(tx); - - if (broadcast) { - const transactionResponse = await provider.sendTransaction(signedTx); - - Lit.Actions.setResponse({ response: transactionResponse.hash }); - } else { - Lit.Actions.setResponse({ response: signedTx }); - } - } catch (err) { - const errorMessage = 'Error: When signing transaction- ' + err.message; - Lit.Actions.setResponse({ response: errorMessage }); } })(); From d92805118768006badf5f6aab125665fe44d9261 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Tue, 1 Oct 2024 22:58:12 +0100 Subject: [PATCH 05/53] refactor(wrapped-keys-lit-actions): LIT-3920 - Refactor `signTransactionWithEncryptedSolanaKey` to use composable functionality - Normalized return values so that `setResponse()` doesn't get called when internal methods return undefined due to `runOnce()` check on getting the decrypted key - Use template strings for string composition --- .../src/lib/common/exportPrivateKey.js | 2 +- .../lib/common/internal/getDecryptedKey.js | 2 +- .../internal/signMessageWithEncryptedKey.js | 4 +- .../signTransactionWithEncryptedKey.js | 17 ++-- .../signMessageWithEncryptedEthereumKey.js | 8 +- ...signTransactionWithEncryptedEthereumKey.js | 4 +- .../internal/signMessageWithEncryptedKey.js | 4 +- .../signTransactionWithEncryptedKey.js | 87 +++++++++++++++++++ .../signMessageWithEncryptedSolanaKey.js | 8 +- .../signTransactionWithEncryptedSolanaKey.js | 84 +++--------------- 10 files changed, 123 insertions(+), 97 deletions(-) create mode 100644 packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransactionWithEncryptedKey.js diff --git a/packages/wrapped-keys-lit-actions/src/lib/common/exportPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/common/exportPrivateKey.js index 01f412d35a..76271db03b 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/common/exportPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/common/exportPrivateKey.js @@ -31,6 +31,6 @@ const { removeSaltFromDecryptedKey } = require('../utils'); const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); Lit.Actions.setResponse({ response: privateKey }); } catch (err) { - Lit.Actions.setResponse({ response: 'Error: ' + err.message }); + Lit.Actions.setResponse({ response: `Error: ${err.message}` }); } })(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKey.js index 1484ab5ea0..74ed0d5f27 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKey.js @@ -17,6 +17,6 @@ export async function getDecryptedKey({ return privateKey; } catch (err) { - throw new Error('When decrypting key to a single node - ' + err.message); + throw new Error(`When decrypting key to a single node - ${err.message}`); } } diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js index 8bbda9d857..b1d943d07c 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js @@ -10,7 +10,7 @@ async function signMessage({ privateKey, messageToSign }) { return { signature, walletAddress: wallet.address }; } catch (err) { - throw new Error('When signing message - ' + err.message); + throw new Error(`When signing message - ${err.message}`); } } @@ -58,5 +58,5 @@ export async function signMessageWithEncryptedKey({ ); } - return { signature, walletAddress }; + return signature; } diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransactionWithEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransactionWithEncryptedKey.js index 175243f116..83972b2b54 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransactionWithEncryptedKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransactionWithEncryptedKey.js @@ -30,7 +30,7 @@ function getValidatedUnsignedTx(unsignedTransaction) { data: unsignedTransaction.dataHex, }; } catch (err) { - throw new Error('Invalid unsignedTransaction - ' + err.message); + throw new Error(`Invalid unsignedTransaction - ${err.message}`); } } @@ -43,7 +43,7 @@ async function getLatestNonce({ walletAddress, chain }) { return nonce; } catch (err) { - throw new Error('Unable to get latest nonce - ' + err.message); + throw new Error(`Unable to get latest nonce - ${err.message}`); } } @@ -55,10 +55,7 @@ async function getEthersRPCProvider({ chain }) { return new ethers.providers.JsonRpcProvider(rpcUrl); } catch (err) { - throw new Error( - 'Getting the rpc for the chain: ${unsignedTransaction.chain} - ' + - err.message - ); + throw new Error(`Getting the rpc for the chain: ${chain} - ${err.message}`); } } @@ -70,7 +67,7 @@ async function getGasPrice({ userProvidedGasPrice, provider }) { return await provider.getGasPrice(); } } catch (err) { - throw new Error('When getting gas price - ' + err.message); + throw new Error(`When getting gas price - ${err.message}`); } } @@ -81,7 +78,7 @@ async function getGasLimit({ provider, userProvidedGasLimit, tx }) { try { return await provider.estimateGas(tx); } catch (err) { - throw new Error('When estimating gas - ' + err.message); + throw new Error(`When estimating gas - ${err.message}`); } } } @@ -90,7 +87,7 @@ async function signTransaction({ tx, wallet }) { try { return await wallet.signTransaction(tx); } catch (err) { - throw new Error('When signing transaction - ' + err.message); + throw new Error(`When signing transaction - ${err.message}`); } } @@ -98,7 +95,7 @@ async function broadcastTransaction({ provider, signedTx }) { try { return await provider.sendTransaction(signedTx); } catch (err) { - throw new Error('When sending transaction - ' + err.message); + throw new Error(`When sending transaction - ${err.message}`); } } diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js index b385200b2a..7ddec2fe59 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js @@ -18,15 +18,17 @@ const { (async () => { try { - const { signature } = await signMessageWithEncryptedKey({ + const signature = await signMessageWithEncryptedKey({ accessControlConditions, ciphertext, dataToEncryptHash, messageToSign, }); - Lit.Actions.setResponse({ response: signature }); + if (signature) { + Lit.Actions.setResponse({ response: signature }); + } } catch (err) { - Lit.Actions.setResponse({ response: 'Error: ' + err.message }); + Lit.Actions.setResponse({ response: `Error: ${err.message}` }); } })(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js index b6a0952eaf..721c862936 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js @@ -27,7 +27,9 @@ const { broadcast, }); - Lit.Actions.setResponse({ response: txResult }); + if (txResult) { + Lit.Actions.setResponse({ response: txResult }); + } } catch (err) { Lit.Actions.setResponse({ response: `Error: ${err.message}`, diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessageWithEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessageWithEncryptedKey.js index 24344f0733..a27b0e5150 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessageWithEncryptedKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessageWithEncryptedKey.js @@ -14,7 +14,7 @@ async function signMessage({ messageToSign, solanaKeyPair }) { return { signature }; } catch (err) { - throw new Error('When signing message - ' + err.message); + throw new Error(`When signing message - ${err.message}`); } } @@ -64,5 +64,5 @@ export async function signMessageWithEncryptedKey({ throw new Error('Signature did not verify to expected Solana public key'); } - return { signature: bs58.encode(signature) }; + return bs58.encode(signature); } diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransactionWithEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransactionWithEncryptedKey.js new file mode 100644 index 0000000000..65f6849aa7 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransactionWithEncryptedKey.js @@ -0,0 +1,87 @@ +import { getDecryptedKey } from '../../common/internal/getDecryptedKey'; +import { removeSaltFromDecryptedKey } from '../../utils'; + +const { + clusterApiUrl, + Connection, + Keypair, + Transaction, +} = require('@solana/web3.js'); + +/* global ethers */ + +function validateUnsignedTransaction(unsignedTransaction) { + const VALID_NETWORKS = ['devnet', 'testnet', 'mainnet-beta']; + + if (!VALID_NETWORKS.includes(unsignedTransaction.chain)) { + throw new Error(`Invalid Solana network: ${unsignedTransaction.chain}`); + } + + if ( + !unsignedTransaction.serializedTransaction || + !unsignedTransaction.serializedTransaction.length === 0 + ) { + throw new Error( + `Invalid serializedTransaction: ${unsignedTransaction.serializedTransaction}` + ); + } +} + +function signTransaction({ solanaKeyPair, transaction }) { + try { + transaction.sign(solanaKeyPair); + + return { signature: ethers.utils.base58.encode(transaction.signature) }; + } catch (err) { + throw new Error(`When signing transaction - ${err.message}`); + } +} + +async function sendTransaction({ chain, transaction }) { + try { + const solanaConnection = new Connection(clusterApiUrl(chain), 'confirmed'); + return await solanaConnection.sendRawTransaction(transaction.serialize()); + } catch (err) { + throw new Error(`When sending transaction - ${err.message}`); + } +} + +export async function signTransactionWithEncryptedSolanaKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + unsignedTransaction, + broadcast, +}) { + validateUnsignedTransaction(unsignedTransaction); + + const decryptedPrivateKey = await getDecryptedKey({ + accessControlConditions, + ciphertext, + dataToEncryptHash, + }); + + if (!decryptedPrivateKey) { + // Silently exit on nodes which didn't run the `decryptToSingleNode` code + return; + } + + const solanaKeyPair = Keypair.fromSecretKey( + Buffer.from(removeSaltFromDecryptedKey(decryptedPrivateKey), 'hex') + ); + + const transaction = Transaction.from( + Buffer.from(unsignedTransaction.serializedTransaction, 'base64') + ); + + const { signature } = signTransaction({ transaction, solanaKeyPair }); + + if (!broadcast) { + return signature; + } else { + return await sendTransaction({ + chain: unsignedTransaction.chain, + transaction, + }); + } +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js index 079dd2bd84..f3d1f9b363 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js @@ -19,15 +19,17 @@ const { (async () => { try { - const { signature } = await signMessageWithEncryptedKey({ + const signature = await signMessageWithEncryptedKey({ accessControlConditions, ciphertext, dataToEncryptHash, messageToSign, }); - Lit.Actions.setResponse({ response: signature }); + if (signature) { + Lit.Actions.setResponse({ response: signature }); + } } catch (err) { - Lit.Actions.setResponse({ response: 'Error: ' + err.message }); + 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 index 25cd879464..581d427916 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js @@ -1,11 +1,8 @@ const { - clusterApiUrl, - Connection, - Keypair, - Transaction, -} = require('@solana/web3.js'); + signTransactionWithEncryptedSolanaKey, +} = require('./internal/signTransactionWithEncryptedKey'); -const { removeSaltFromDecryptedKey } = require('../utils'); +/* global accessControlConditions, ciphertext, dataToEncryptHash, unsignedTransaction, Lit, broadcast */ /** * @@ -22,82 +19,21 @@ const { removeSaltFromDecryptedKey } = require('../utils'); */ (async () => { - const VALID_NETWORKS = ['devnet', 'testnet', 'mainnet-beta']; - - if (!VALID_NETWORKS.includes(unsignedTransaction.chain)) { - Lit.Actions.setResponse({ - response: `Error: Invalid Solana network: ${unsignedTransaction.chain}`, - }); - return; - } - - if ( - !unsignedTransaction.serializedTransaction || - !unsignedTransaction.serializedTransaction.length === 0 - ) { - Lit.Actions.setResponse({ - response: `Error: Invalid serializedTransaction: ${unsignedTransaction.serializedTransaction}`, - }); - return; - } - - let decryptedPrivateKey; try { - decryptedPrivateKey = await Lit.Actions.decryptToSingleNode({ + const txResult = await signTransactionWithEncryptedSolanaKey({ accessControlConditions, - chain: 'ethereum', ciphertext, dataToEncryptHash, - authSig: null, + unsignedTransaction, + broadcast, }); - } catch (error) { - Lit.Actions.setResponse({ - response: `Error: When decrypting data to private key: ${error.message}`, - }); - return; - } - if (!decryptedPrivateKey) { - // Exit the nodes which don't have the decryptedData - return; - } - - let privateKey; - try { - privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); - } catch (err) { - Lit.Actions.setResponse({ response: err.message }); - return; - } - - const solanaKeyPair = Keypair.fromSecretKey( - Uint8Array.from(Buffer.from(privateKey, 'hex')) - ); - - try { - const transaction = Transaction.from( - Buffer.from(unsignedTransaction.serializedTransaction, 'base64') - ); - - transaction.sign(solanaKeyPair); - const signature = ethers.utils.base58.encode(transaction.signature); - - if (broadcast) { - const solanaConnection = new Connection( - clusterApiUrl(unsignedTransaction.chain), - 'confirmed' - ); - const transactionSignature = await solanaConnection.sendRawTransaction( - transaction.serialize() - ); - - Lit.Actions.setResponse({ response: transactionSignature }); - } else { - Lit.Actions.setResponse({ response: signature }); + if (txResult) { + Lit.Actions.setResponse({ response: txResult }); } - } catch (error) { + } catch (err) { Lit.Actions.setResponse({ - response: `Error: During transaction signing and submission: ${error.message}`, + response: `Error: ${err.message}`, }); } })(); From 9f9a67025879bda534bf5e05b8a761e5ec04c706 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Tue, 1 Oct 2024 23:17:47 +0100 Subject: [PATCH 06/53] refactor(wrapped-keys-lit-actions): LIT-3920 - Extract usage of `getDecryptedKey()` to entrypoint logic and pass `privateKey` into child flows for signing messages and transactions - Rename `generateXXXX` methods that encrypt the generated keys in preparation of separating key generation from key encryption --- .../generateEncryptedEthereumPrivateKey.js | 6 ++-- .../internal/generateEncryptedPrivateKey.js | 4 ++- .../internal/signMessageWithEncryptedKey.js | 22 ++----------- .../signTransactionWithEncryptedKey.js | 18 ++--------- .../signMessageWithEncryptedEthereumKey.js | 25 ++++++++++----- ...signTransactionWithEncryptedEthereumKey.js | 21 ++++++++++--- .../generateEncryptedSolanaPrivateKey.js | 7 +++-- .../internal/generateEncryptedPrivateKey.js | 4 ++- .../internal/signMessageWithEncryptedKey.js | 21 ++----------- .../signTransactionWithEncryptedKey.js | 31 +++++-------------- .../signMessageWithEncryptedSolanaKey.js | 25 ++++++++++----- .../signTransactionWithEncryptedSolanaKey.js | 21 ++++++++++--- 12 files changed, 98 insertions(+), 107 deletions(-) diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js index bf5e9aa5ea..11a5c6f94f 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js @@ -9,13 +9,15 @@ * * @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/generateEncryptedPrivateKey'; +import { generateEncryptedEthereumPrivateKey } from './internal/generateEncryptedPrivateKey'; (async () => { const generatedKeyResultStr = await Lit.Actions.runOnce( { waitForResponse: true, name: 'generateEthereumPrivateKey' }, async () => - JSON.stringify(generateEthereumPrivateKey({ accessControlConditions })) + JSON.stringify( + generateEncryptedEthereumPrivateKey({ accessControlConditions }) + ) ); Lit.Actions.setResponse({ diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generateEncryptedPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generateEncryptedPrivateKey.js index 10f437d297..161943f2e9 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generateEncryptedPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generateEncryptedPrivateKey.js @@ -9,7 +9,9 @@ */ import { LIT_PREFIX } from '../../constants'; -export async function generateEthereumPrivateKey({ accessControlConditions }) { +export async function generateEncryptedEthereumPrivateKey({ + accessControlConditions, +}) { const wallet = ethers.Wallet.createRandom(); const privateKey = LIT_PREFIX + wallet.privateKey.toString(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js index b1d943d07c..d127f3c553 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js @@ -1,8 +1,5 @@ /* global ethers */ -import { getDecryptedKey } from '../../common/internal/getDecryptedKey'; -import { removeSaltFromDecryptedKey } from '../../utils'; - async function signMessage({ privateKey, messageToSign }) { try { const wallet = new ethers.Wallet(privateKey); @@ -22,25 +19,10 @@ function verifyMessageSignature(messageToSign, signature) { } } -export async function signMessageWithEncryptedKey({ - accessControlConditions, - ciphertext, - dataToEncryptHash, +export async function signMessageWithEncryptedEthereumKey({ + privateKey, messageToSign, }) { - const decryptedPrivateKey = await getDecryptedKey({ - accessControlConditions, - ciphertext, - dataToEncryptHash, - }); - - if (!decryptedPrivateKey) { - // Silently exit on nodes which didn't run the `decryptToSingleNode` code - return; - } - - const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); - const { signature, walletAddress } = await signMessage({ privateKey, messageToSign, diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransactionWithEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransactionWithEncryptedKey.js index 83972b2b54..a5bc880b91 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransactionWithEncryptedKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransactionWithEncryptedKey.js @@ -100,26 +100,12 @@ async function broadcastTransaction({ provider, signedTx }) { } export async function signTransactionWithEncryptedKey({ - accessControlConditions, - ciphertext, - dataToEncryptHash, - unsignedTransaction, broadcast, + privateKey, + unsignedTransaction, }) { const tx = getValidatedUnsignedTx(unsignedTransaction); - const decryptedPrivateKey = await getDecryptedKey({ - accessControlConditions, - ciphertext, - dataToEncryptHash, - }); - - if (!decryptedPrivateKey) { - // Silently exit on nodes which didn't run the `decryptToSingleNode` code - return; - } - - const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); const wallet = new ethers.Wallet(privateKey); tx.from = wallet.address; diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js index 7ddec2fe59..9a9900cf31 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js @@ -1,8 +1,10 @@ -/* global accessControlConditions, ciphertext, dataToEncryptHash, messageToSign, Lit */ - const { - signMessageWithEncryptedKey, + signMessageWithEncryptedEthereumKey, } = require('./internal/signMessageWithEncryptedKey'); +const { getDecryptedKey } = require('../common/internal/getDecryptedKey'); +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. @@ -18,16 +20,25 @@ const { (async () => { try { - const signature = await signMessageWithEncryptedKey({ + const decryptedPrivateKey = await getDecryptedKey({ accessControlConditions, ciphertext, dataToEncryptHash, - messageToSign, }); - if (signature) { - Lit.Actions.setResponse({ response: signature }); + if (!decryptedPrivateKey) { + // Silently exit on nodes which didn't run the `decryptToSingleNode` code + return; } + + const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); + + const signature = await signMessageWithEncryptedEthereumKey({ + 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/ethereum/signTransactionWithEncryptedEthereumKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js index 721c862936..980fa8eebe 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js @@ -1,6 +1,8 @@ const { signTransactionWithEncryptedKey, } = require('./internal/signTransactionWithEncryptedKey'); +const { getDecryptedKey } = require('../common/internal/getDecryptedKey'); +const { removeSaltFromDecryptedKey } = require('../utils'); /* global accessControlConditions, ciphertext, dataToEncryptHash, unsignedTransaction, broadcast, Lit */ @@ -19,17 +21,26 @@ const { */ (async () => { try { - const txResult = await signTransactionWithEncryptedKey({ + const decryptedPrivateKey = await getDecryptedKey({ accessControlConditions, ciphertext, dataToEncryptHash, - unsignedTransaction, - broadcast, }); - if (txResult) { - Lit.Actions.setResponse({ response: txResult }); + if (!decryptedPrivateKey) { + // Silently exit on nodes which didn't run the `decryptToSingleNode` code + return; } + + const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); + + const txResult = await signTransactionWithEncryptedKey({ + 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/src/lib/solana/generateEncryptedSolanaPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js index 5884c8dba8..d55f15c9c9 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js @@ -1,5 +1,5 @@ const { - generateSolanaPrivateKey, + generateEncryptedSolanaPrivateKey, } = require('./internal/generateEncryptedPrivateKey'); /* global accessControlConditions, Lit */ @@ -16,7 +16,10 @@ const { (async () => { const generatedKeyResultStr = await Lit.Actions.runOnce( { waitForResponse: true, name: 'generateSolanaPrivateKey' }, - () => JSON.stringify(generateSolanaPrivateKey({ accessControlConditions })) + () => + JSON.stringify( + generateEncryptedSolanaPrivateKey({ accessControlConditions }) + ) ); Lit.Actions.setResponse({ diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generateEncryptedPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generateEncryptedPrivateKey.js index f677dc26dc..77d479e7ed 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generateEncryptedPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generateEncryptedPrivateKey.js @@ -11,7 +11,9 @@ import { LIT_PREFIX } from '../../constants'; * @private * @returns { Promise<{ciphertext: string, dataToEncryptHash: string, publicKey: string}> } - The ciphertext & dataToEncryptHash which are the result of the encryption, and the publicKey of the newly generated Ethers Wrapped Key. */ -export async function generateSolanaPrivateKey({ accessControlConditions }) { +export async function generateEncryptedSolanaPrivateKey({ + accessControlConditions, +}) { const solanaKeypair = Keypair.generate(); const privateKey = LIT_PREFIX + Buffer.from(solanaKeypair.secretKey).toString('hex'); diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessageWithEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessageWithEncryptedKey.js index a27b0e5150..e387ee9f63 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessageWithEncryptedKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessageWithEncryptedKey.js @@ -32,26 +32,11 @@ function verifyMessageSignature({ signature, solanaKeyPair, messageToSign }) { } } -export async function signMessageWithEncryptedKey({ - accessControlConditions, - ciphertext, - dataToEncryptHash, +export async function signMessageWithEncryptedSolanaKey({ messageToSign, + privateKey, }) { - const decryptedPrivateKey = await getDecryptedKey({ - accessControlConditions, - ciphertext, - dataToEncryptHash, - }); - - if (!decryptedPrivateKey) { - // Silently exit on nodes which didn't run the `decryptToSingleNode` code - return; - } - - const solanaKeyPair = Keypair.fromSecretKey( - Buffer.from(removeSaltFromDecryptedKey(decryptedPrivateKey), 'hex') - ); + const solanaKeyPair = Keypair.fromSecretKey(Buffer.from(privateKey, 'hex')); const { signature } = await signMessage({ messageToSign, diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransactionWithEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransactionWithEncryptedKey.js index 65f6849aa7..ece047a04e 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransactionWithEncryptedKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransactionWithEncryptedKey.js @@ -47,28 +47,13 @@ async function sendTransaction({ chain, transaction }) { } export async function signTransactionWithEncryptedSolanaKey({ - accessControlConditions, - ciphertext, - dataToEncryptHash, - unsignedTransaction, broadcast, + privateKey, + unsignedTransaction, }) { validateUnsignedTransaction(unsignedTransaction); - const decryptedPrivateKey = await getDecryptedKey({ - accessControlConditions, - ciphertext, - dataToEncryptHash, - }); - - if (!decryptedPrivateKey) { - // Silently exit on nodes which didn't run the `decryptToSingleNode` code - return; - } - - const solanaKeyPair = Keypair.fromSecretKey( - Buffer.from(removeSaltFromDecryptedKey(decryptedPrivateKey), 'hex') - ); + const solanaKeyPair = Keypair.fromSecretKey(Buffer.from(privateKey, 'hex')); const transaction = Transaction.from( Buffer.from(unsignedTransaction.serializedTransaction, 'base64') @@ -78,10 +63,10 @@ export async function signTransactionWithEncryptedSolanaKey({ if (!broadcast) { return signature; - } else { - return await sendTransaction({ - chain: unsignedTransaction.chain, - transaction, - }); } + + return await sendTransaction({ + chain: unsignedTransaction.chain, + transaction, + }); } diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js index f3d1f9b363..1949da66e4 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js @@ -1,8 +1,10 @@ -/* global accessControlConditions, ciphertext, dataToEncryptHash, messageToSign, Lit */ - const { - signMessageWithEncryptedKey, + signMessageWithEncryptedSolanaKey, } = require('./internal/signMessageWithEncryptedKey'); +const { getDecryptedKey } = require('../common/internal/getDecryptedKey'); +const { removeSaltFromDecryptedKey } = require('../utils'); + +/* global accessControlConditions, ciphertext, dataToEncryptHash, messageToSign, Lit */ /** * @@ -19,16 +21,25 @@ const { (async () => { try { - const signature = await signMessageWithEncryptedKey({ + const decryptedPrivateKey = await getDecryptedKey({ accessControlConditions, ciphertext, dataToEncryptHash, - messageToSign, }); - if (signature) { - Lit.Actions.setResponse({ response: signature }); + if (!decryptedPrivateKey) { + // Silently exit on nodes which didn't run the `decryptToSingleNode` code + return; } + + const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); + + const signature = await signMessageWithEncryptedSolanaKey({ + 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 index 581d427916..382828445f 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js @@ -1,6 +1,8 @@ const { signTransactionWithEncryptedSolanaKey, } = require('./internal/signTransactionWithEncryptedKey'); +const { getDecryptedKey } = require('../common/internal/getDecryptedKey'); +const { removeSaltFromDecryptedKey } = require('../utils'); /* global accessControlConditions, ciphertext, dataToEncryptHash, unsignedTransaction, Lit, broadcast */ @@ -20,17 +22,26 @@ const { (async () => { try { - const txResult = await signTransactionWithEncryptedSolanaKey({ + const decryptedPrivateKey = await getDecryptedKey({ accessControlConditions, ciphertext, dataToEncryptHash, - unsignedTransaction, - broadcast, }); - if (txResult) { - Lit.Actions.setResponse({ response: txResult }); + if (!decryptedPrivateKey) { + // Silently exit on nodes which didn't run the `decryptToSingleNode` code + return; } + + const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); + + const txResult = await signTransactionWithEncryptedSolanaKey({ + broadcast, + privateKey, + unsignedTransaction, + }); + + Lit.Actions.setResponse({ response: txResult }); } catch (err) { Lit.Actions.setResponse({ response: `Error: ${err.message}`, From a294bc43e605c2ce7e19aa1ce5904d8e6e00b06e Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Tue, 1 Oct 2024 23:32:10 +0100 Subject: [PATCH 07/53] refactor(wrapped-keys-lit-actions): LIT-3920 - Extract private key generation from _encrypted_ private key generation for direct use of generated, un-encrypted keys for signing messages in new executeWrappedKeyOperations method --- .../src/lib/common/internal/getDecryptedKey.js | 4 +--- .../internal/generateEncryptedPrivateKey.js | 16 +++++++--------- .../lib/ethereum/internal/generatePrivateKey.js | 10 ++++++++++ .../internal/generateEncryptedPrivateKey.js | 13 ++++--------- .../lib/solana/internal/generatePrivateKey.js | 10 ++++++++++ 5 files changed, 32 insertions(+), 21 deletions(-) create mode 100644 packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generatePrivateKey.js create mode 100644 packages/wrapped-keys-lit-actions/src/lib/solana/internal/generatePrivateKey.js diff --git a/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKey.js index 74ed0d5f27..ba5d809a29 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKey.js @@ -7,15 +7,13 @@ export async function getDecryptedKey({ }) { try { // May be undefined, since we're using `decryptToSingleNode` - const privateKey = await Lit.Actions.decryptToSingleNode({ + return await Lit.Actions.decryptToSingleNode({ accessControlConditions, ciphertext, dataToEncryptHash, chain: 'ethereum', authSig: null, }); - - return privateKey; } 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/internal/generateEncryptedPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generateEncryptedPrivateKey.js index 161943f2e9..b1801353e1 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generateEncryptedPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generateEncryptedPrivateKey.js @@ -1,4 +1,7 @@ -/* global ethers, Lit */ +import { generateEthereumPrivateKey } from './generatePrivateKey'; +import { LIT_PREFIX } from '../../constants'; + +/* global Lit */ /** * Generates a random Ethers private key that only allows the provided PKP to decrypt it @@ -7,25 +10,20 @@ * @private * @returns { Promise<{ciphertext: string, dataToEncryptHash: string, publicKey: string}> } - The ciphertext & dataToEncryptHash which are the result of the encryption, and the publicKey of the newly generated Ethers Wrapped Key. */ -import { LIT_PREFIX } from '../../constants'; export async function generateEncryptedEthereumPrivateKey({ accessControlConditions, }) { - const wallet = ethers.Wallet.createRandom(); - const privateKey = LIT_PREFIX + wallet.privateKey.toString(); - - let utf8Encode = new TextEncoder(); - const to_encrypt = utf8Encode.encode(privateKey); + const { privateKey, publicKey } = generateEthereumPrivateKey(); const { ciphertext, dataToEncryptHash } = await Lit.Actions.encrypt({ accessControlConditions, - to_encrypt, + to_encrypt: new TextEncoder().encode(LIT_PREFIX + privateKey), }); return { ciphertext, dataToEncryptHash, - publicKey: wallet.publicKey, + publicKey, }; } diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generatePrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generatePrivateKey.js new file mode 100644 index 0000000000..299816e039 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generatePrivateKey.js @@ -0,0 +1,10 @@ +/* global ethers */ + +export function generateEthereumPrivateKey() { + const wallet = ethers.Wallet.createRandom(); + + return { + privateKey: wallet.privateKey.toString(), + publicKey: wallet.publicKey, + }; +} diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generateEncryptedPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generateEncryptedPrivateKey.js index 77d479e7ed..f0aa305481 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generateEncryptedPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generateEncryptedPrivateKey.js @@ -1,5 +1,4 @@ -import { Keypair } from '@solana/web3.js'; - +import { generateSolanaPrivateKey } from './generatePrivateKey'; import { LIT_PREFIX } from '../../constants'; /* global Lit */ @@ -14,20 +13,16 @@ import { LIT_PREFIX } from '../../constants'; export async function generateEncryptedSolanaPrivateKey({ accessControlConditions, }) { - const solanaKeypair = Keypair.generate(); - const privateKey = - LIT_PREFIX + Buffer.from(solanaKeypair.secretKey).toString('hex'); - let utf8Encode = new TextEncoder(); - const to_encrypt = utf8Encode.encode(privateKey); + const { privateKey, publicKey } = generateSolanaPrivateKey(); const { ciphertext, dataToEncryptHash } = await Lit.Actions.encrypt({ accessControlConditions, - to_encrypt, + to_encrypt: new TextEncoder().encode(LIT_PREFIX + privateKey), }); return { ciphertext, dataToEncryptHash, - publicKey: solanaKeypair.publicKey.toString(), + publicKey, }; } diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generatePrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generatePrivateKey.js new file mode 100644 index 0000000000..35c0a4ec46 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generatePrivateKey.js @@ -0,0 +1,10 @@ +import { Keypair } from '@solana/web3.js'; + +export function generateSolanaPrivateKey() { + const solanaKeypair = Keypair.generate(); + + return { + privateKey: Buffer.from(solanaKeypair.secretKey).toString('hex'), + publicKey: solanaKeypair.publicKey.toString(), + }; +} From 231ac2b986319f56db5cafbe743497f8eedb57bb Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 2 Oct 2024 00:09:56 +0100 Subject: [PATCH 08/53] refactor(wrapped-keys-lit-actions): LIT-3920 - Extract encryption of keys to common `encryptPrivateKey` method and rename `signMessage...` and `signTransaction...` methods to clarify that they use _already decrypted_ keys --- .../internal/encryptKey.js} | 11 ++------ .../generateEncryptedEthereumPrivateKey.js | 17 +++++++---- ...sageWithEncryptedKey.js => signMessage.js} | 5 +--- ...WithEncryptedKey.js => signTransaction.js} | 2 +- .../signMessageWithEncryptedEthereumKey.js | 6 ++-- ...signTransactionWithEncryptedEthereumKey.js | 6 ++-- .../generateEncryptedSolanaPrivateKey.js | 20 +++++++------ .../internal/generateEncryptedPrivateKey.js | 28 ------------------- ...sageWithEncryptedKey.js => signMessage.js} | 8 +----- ...WithEncryptedKey.js => signTransaction.js} | 2 +- .../signMessageWithEncryptedSolanaKey.js | 6 ++-- .../signTransactionWithEncryptedSolanaKey.js | 6 ++-- 12 files changed, 39 insertions(+), 78 deletions(-) rename packages/wrapped-keys-lit-actions/src/lib/{ethereum/internal/generateEncryptedPrivateKey.js => common/internal/encryptKey.js} (60%) rename packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/{signMessageWithEncryptedKey.js => signMessage.js} (91%) rename packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/{signTransactionWithEncryptedKey.js => signTransaction.js} (98%) delete mode 100644 packages/wrapped-keys-lit-actions/src/lib/solana/internal/generateEncryptedPrivateKey.js rename packages/wrapped-keys-lit-actions/src/lib/solana/internal/{signMessageWithEncryptedKey.js => signMessage.js} (83%) rename packages/wrapped-keys-lit-actions/src/lib/solana/internal/{signTransactionWithEncryptedKey.js => signTransaction.js} (96%) diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generateEncryptedPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/common/internal/encryptKey.js similarity index 60% rename from packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generateEncryptedPrivateKey.js rename to packages/wrapped-keys-lit-actions/src/lib/common/internal/encryptKey.js index b1801353e1..42af63a266 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/generateEncryptedPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/common/internal/encryptKey.js @@ -1,21 +1,16 @@ -import { generateEthereumPrivateKey } from './generatePrivateKey'; import { LIT_PREFIX } from '../../constants'; /* global Lit */ /** - * Generates a random Ethers private key that only allows the provided PKP to decrypt it - * This should be executed using `runOnce` to avoid generating `n` new private keys where we only want 1. - * * @private * @returns { Promise<{ciphertext: string, dataToEncryptHash: string, publicKey: string}> } - The ciphertext & dataToEncryptHash which are the result of the encryption, and the publicKey of the newly generated Ethers Wrapped Key. */ - -export async function generateEncryptedEthereumPrivateKey({ +export async function encryptPrivateKey({ accessControlConditions, + privateKey, + publicKey, }) { - const { privateKey, publicKey } = generateEthereumPrivateKey(); - const { ciphertext, dataToEncryptHash } = await Lit.Actions.encrypt({ accessControlConditions, to_encrypt: new TextEncoder().encode(LIT_PREFIX + privateKey), diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js index 11a5c6f94f..3944c61605 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js @@ -9,18 +9,25 @@ * * @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 { generateEncryptedEthereumPrivateKey } from './internal/generateEncryptedPrivateKey'; +import { generateEthereumPrivateKey } from './internal/generatePrivateKey'; +import { encryptPrivateKey } from '../common/internal/encryptKey'; (async () => { - const generatedKeyResultStr = await Lit.Actions.runOnce( - { waitForResponse: true, name: 'generateEthereumPrivateKey' }, + const { privateKey, publicKey } = generateEthereumPrivateKey(); + + const encryptedKeyResultStr = await Lit.Actions.runOnce( + { waitForResponse: true, name: 'encryptEthereumPrivateKey' }, async () => JSON.stringify( - generateEncryptedEthereumPrivateKey({ accessControlConditions }) + encryptPrivateKey({ + accessControlConditions, + privateKey, + publicKey, + }) ) ); Lit.Actions.setResponse({ - response: generatedKeyResultStr, + response: encryptedKeyResultStr, }); })(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessage.js similarity index 91% rename from packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js rename to packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessage.js index d127f3c553..88e5109dbb 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessageWithEncryptedKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessage.js @@ -19,10 +19,7 @@ function verifyMessageSignature(messageToSign, signature) { } } -export async function signMessageWithEncryptedEthereumKey({ - privateKey, - messageToSign, -}) { +export async function signMessageEthereumKey({ privateKey, messageToSign }) { const { signature, walletAddress } = await signMessage({ privateKey, messageToSign, diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransactionWithEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransaction.js similarity index 98% rename from packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransactionWithEncryptedKey.js rename to packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransaction.js index a5bc880b91..232b294073 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransactionWithEncryptedKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransaction.js @@ -99,7 +99,7 @@ async function broadcastTransaction({ provider, signedTx }) { } } -export async function signTransactionWithEncryptedKey({ +export async function signTransactionEthereumKey({ broadcast, privateKey, unsignedTransaction, diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js index 9a9900cf31..fe89b5d6ec 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js @@ -1,6 +1,4 @@ -const { - signMessageWithEncryptedEthereumKey, -} = require('./internal/signMessageWithEncryptedKey'); +const { signMessageEthereumKey } = require('./internal/signMessage'); const { getDecryptedKey } = require('../common/internal/getDecryptedKey'); const { removeSaltFromDecryptedKey } = require('../utils'); @@ -33,7 +31,7 @@ const { removeSaltFromDecryptedKey } = require('../utils'); const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); - const signature = await signMessageWithEncryptedEthereumKey({ + const signature = await signMessageEthereumKey({ privateKey, messageToSign, }); diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js index 980fa8eebe..8c7028cfe5 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js @@ -1,6 +1,4 @@ -const { - signTransactionWithEncryptedKey, -} = require('./internal/signTransactionWithEncryptedKey'); +const { signTransactionEthereumKey } = require('./internal/signTransaction'); const { getDecryptedKey } = require('../common/internal/getDecryptedKey'); const { removeSaltFromDecryptedKey } = require('../utils'); @@ -34,7 +32,7 @@ const { removeSaltFromDecryptedKey } = require('../utils'); const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); - const txResult = await signTransactionWithEncryptedKey({ + const txResult = await signTransactionEthereumKey({ broadcast, privateKey, unsignedTransaction, diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js index d55f15c9c9..2c00b728e0 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js @@ -1,11 +1,9 @@ -const { - generateEncryptedSolanaPrivateKey, -} = require('./internal/generateEncryptedPrivateKey'); +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 @@ -14,15 +12,21 @@ const { * @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 generatedKeyResultStr = await Lit.Actions.runOnce( - { waitForResponse: true, name: 'generateSolanaPrivateKey' }, + const { privateKey, publicKey } = generateSolanaPrivateKey(); + + const encryptedKeyResultStr = await Lit.Actions.runOnce( + { waitForResponse: true, name: 'encryptSolanaPrivateKey' }, () => JSON.stringify( - generateEncryptedSolanaPrivateKey({ accessControlConditions }) + encryptPrivateKey({ + accessControlConditions, + publicKey, + privateKey, + }) ) ); Lit.Actions.setResponse({ - response: generatedKeyResultStr, + response: encryptedKeyResultStr, }); })(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generateEncryptedPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generateEncryptedPrivateKey.js deleted file mode 100644 index f0aa305481..0000000000 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/generateEncryptedPrivateKey.js +++ /dev/null @@ -1,28 +0,0 @@ -import { generateSolanaPrivateKey } from './generatePrivateKey'; -import { LIT_PREFIX } from '../../constants'; - -/* global 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 - * This should be executed using `runOnce` to avoid generating `n` new private keys where we only want 1. - * - * @private - * @returns { Promise<{ciphertext: string, dataToEncryptHash: string, publicKey: string}> } - The ciphertext & dataToEncryptHash which are the result of the encryption, and the publicKey of the newly generated Ethers Wrapped Key. - */ -export async function generateEncryptedSolanaPrivateKey({ - accessControlConditions, -}) { - const { privateKey, publicKey } = generateSolanaPrivateKey(); - - const { ciphertext, dataToEncryptHash } = await Lit.Actions.encrypt({ - accessControlConditions, - to_encrypt: new TextEncoder().encode(LIT_PREFIX + privateKey), - }); - - return { - ciphertext, - dataToEncryptHash, - publicKey, - }; -} diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessageWithEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessage.js similarity index 83% rename from packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessageWithEncryptedKey.js rename to packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessage.js index e387ee9f63..fbb539541c 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessageWithEncryptedKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessage.js @@ -2,9 +2,6 @@ import { Keypair } from '@solana/web3.js'; import bs58 from 'bs58'; import nacl from 'tweetnacl'; -import { getDecryptedKey } from '../../common/internal/getDecryptedKey'; -import { removeSaltFromDecryptedKey } from '../../utils'; - async function signMessage({ messageToSign, solanaKeyPair }) { try { const signature = nacl.sign.detached( @@ -32,10 +29,7 @@ function verifyMessageSignature({ signature, solanaKeyPair, messageToSign }) { } } -export async function signMessageWithEncryptedSolanaKey({ - messageToSign, - privateKey, -}) { +export async function signMessageSolanaKey({ messageToSign, privateKey }) { const solanaKeyPair = Keypair.fromSecretKey(Buffer.from(privateKey, 'hex')); const { signature } = await signMessage({ diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransactionWithEncryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransaction.js similarity index 96% rename from packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransactionWithEncryptedKey.js rename to packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransaction.js index ece047a04e..305e1f9451 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransactionWithEncryptedKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransaction.js @@ -46,7 +46,7 @@ async function sendTransaction({ chain, transaction }) { } } -export async function signTransactionWithEncryptedSolanaKey({ +export async function signTransactionSolanaKey({ broadcast, privateKey, unsignedTransaction, diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js index 1949da66e4..6105cdac19 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js @@ -1,6 +1,4 @@ -const { - signMessageWithEncryptedSolanaKey, -} = require('./internal/signMessageWithEncryptedKey'); +const { signMessageSolanaKey } = require('./internal/signMessage'); const { getDecryptedKey } = require('../common/internal/getDecryptedKey'); const { removeSaltFromDecryptedKey } = require('../utils'); @@ -34,7 +32,7 @@ const { removeSaltFromDecryptedKey } = require('../utils'); const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); - const signature = await signMessageWithEncryptedSolanaKey({ + const signature = await signMessageSolanaKey({ messageToSign, privateKey, }); diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js index 382828445f..5dc4b7d592 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js @@ -1,6 +1,4 @@ -const { - signTransactionWithEncryptedSolanaKey, -} = require('./internal/signTransactionWithEncryptedKey'); +const { signTransactionSolanaKey } = require('./internal/signTransaction'); const { getDecryptedKey } = require('../common/internal/getDecryptedKey'); const { removeSaltFromDecryptedKey } = require('../utils'); @@ -35,7 +33,7 @@ const { removeSaltFromDecryptedKey } = require('../utils'); const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey); - const txResult = await signTransactionWithEncryptedSolanaKey({ + const txResult = await signTransactionSolanaKey({ broadcast, privateKey, unsignedTransaction, From 65b3a636b529eb38bcfa746e84c94b616db434f0 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Thu, 3 Oct 2024 23:46:40 +0100 Subject: [PATCH 09/53] refactor(wrapped-keys-lit-actions): LIT-3920 - Normalize function signature for `verifyMessageSignature()` and `signMessage()` --- .../src/lib/ethereum/internal/signMessage.js | 12 +++++------- .../src/lib/solana/internal/signMessage.js | 14 ++++++++++---- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessage.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessage.js index 88e5109dbb..5cc30864f0 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessage.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signMessage.js @@ -11,11 +11,13 @@ async function signMessage({ privateKey, messageToSign }) { } } -function verifyMessageSignature(messageToSign, signature) { +function verifyMessageSignature({ messageToSign, signature }) { try { return ethers.utils.verifyMessage(messageToSign, signature); } catch (err) { - throw new Error(`When validating signed message is valid: ${err.message}`); + throw new Error( + `When validating signed Ethereum message is valid: ${err.message}` + ); } } @@ -25,11 +27,7 @@ export async function signMessageEthereumKey({ privateKey, messageToSign }) { messageToSign, }); - const recoveredAddress = verifyMessageSignature( - messageToSign, - signature, - walletAddress - ); + const recoveredAddress = verifyMessageSignature({ messageToSign, signature }); if (recoveredAddress !== walletAddress) { throw new Error( diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessage.js b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessage.js index fbb539541c..a44ec7b86a 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessage.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessage.js @@ -2,7 +2,7 @@ import { Keypair } from '@solana/web3.js'; import bs58 from 'bs58'; import nacl from 'tweetnacl'; -async function signMessage({ messageToSign, solanaKeyPair }) { +function signMessage({ messageToSign, solanaKeyPair }) { try { const signature = nacl.sign.detached( new TextEncoder().encode(messageToSign), @@ -25,19 +25,25 @@ function verifyMessageSignature({ signature, solanaKeyPair, messageToSign }) { return isValid; } catch (err) { - throw new Error(`When validating signed message is valid: ${err.message}`); + throw new Error( + `When validating signed Solana message is valid: ${err.message}` + ); } } export async function signMessageSolanaKey({ messageToSign, privateKey }) { const solanaKeyPair = Keypair.fromSecretKey(Buffer.from(privateKey, 'hex')); - const { signature } = await signMessage({ + const { signature } = signMessage({ messageToSign, solanaKeyPair, }); - const isValid = verifyMessageSignature({ signature, solanaKeyPair }); + const isValid = verifyMessageSignature({ + signature, + solanaKeyPair, + messageToSign, + }); if (!isValid) { throw new Error('Signature did not verify to expected Solana public key'); From a72e1593a3d5c636fa02ac2bff74f5fd0b4b0c0f Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Thu, 3 Oct 2024 23:47:26 +0100 Subject: [PATCH 10/53] fix(wrapped-keys-lit-actions): LIT-3920 - Add missing `await` in generate methods --- .../src/lib/ethereum/generateEncryptedEthereumPrivateKey.js | 2 +- .../src/lib/solana/generateEncryptedSolanaPrivateKey.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js index 3944c61605..1abd82c20d 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js @@ -19,7 +19,7 @@ import { encryptPrivateKey } from '../common/internal/encryptKey'; { waitForResponse: true, name: 'encryptEthereumPrivateKey' }, async () => JSON.stringify( - encryptPrivateKey({ + await encryptPrivateKey({ accessControlConditions, privateKey, publicKey, diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js index 2c00b728e0..ae03089815 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js @@ -16,9 +16,9 @@ const { encryptPrivateKey } = require('../common/internal/encryptKey'); const encryptedKeyResultStr = await Lit.Actions.runOnce( { waitForResponse: true, name: 'encryptSolanaPrivateKey' }, - () => + async () => JSON.stringify( - encryptPrivateKey({ + await encryptPrivateKey({ accessControlConditions, publicKey, privateKey, From c29458784a914a4a4fd1606fac04190f9943385c Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Tue, 8 Oct 2024 21:05:12 +0100 Subject: [PATCH 11/53] test(wrapped-keys-lit-actions): LIT-3920 - Update Solana sign transaction test to expect base58 signature response (#652) --- .../wrapped-keys/testSignTransactionWithSolanaEncryptedKey.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/local-tests/tests/wrapped-keys/testSignTransactionWithSolanaEncryptedKey.ts b/local-tests/tests/wrapped-keys/testSignTransactionWithSolanaEncryptedKey.ts index f0ac9282cd..55f9bfa402 100644 --- a/local-tests/tests/wrapped-keys/testSignTransactionWithSolanaEncryptedKey.ts +++ b/local-tests/tests/wrapped-keys/testSignTransactionWithSolanaEncryptedKey.ts @@ -11,6 +11,7 @@ import { clusterApiUrl, } from '@solana/web3.js'; import { getPkpSessionSigs } from 'local-tests/setup/session-sigs/get-pkp-session-sigs'; +import { ethers } from 'ethers'; const { importPrivateKey, signTransactionWithEncryptedKey } = api; @@ -125,7 +126,7 @@ export const testSignTransactionWithSolanaEncryptedKey = async ( // const confirmation = await solanaConnection.confirmTransaction(signedTx); // console.log(confirmation); // { context: { slot: 321490379 }, value: { err: null } } - const signatureBuffer = Buffer.from(signedTx, 'base64'); + const signatureBuffer = Buffer.from(ethers.utils.base58.decode(signedTx)); solanaTransaction.addSignature(solanaKeypair.publicKey, signatureBuffer); if (!solanaTransaction.verifySignatures()) { From fe9b60b72d662a4a111cfedd298533da67ce5c33 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Tue, 8 Oct 2024 21:07:05 +0100 Subject: [PATCH 12/53] test(wrapped-keys-lit-actions): LIT-3920 - Ensure private key used to get funds is released after it is used in tinny `getFunds()` - Removed logging all private keys in env Just In Case :tm: since this might be run with real PKeys we don't want to be logging gratuitously --- local-tests/setup/tinny-environment.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/local-tests/setup/tinny-environment.ts b/local-tests/setup/tinny-environment.ts index 21daf4c6c9..50c2c5552c 100644 --- a/local-tests/setup/tinny-environment.ts +++ b/local-tests/setup/tinny-environment.ts @@ -181,7 +181,6 @@ export class TinnyEnvironment { return { privateKey: this.processEnvs.PRIVATE_KEYS[index], index }; // Return the key and its index } else { console.log('[𐬺🧪 Tinny Environment𐬺] No available keys. Waiting...', { - privateKeys: this.processEnvs.PRIVATE_KEYS, keysInUse: this.processEnvs.KEY_IN_USE, }); // Log a message indicating that we are waiting // Wait for the specified interval before checking again @@ -447,8 +446,9 @@ export class TinnyEnvironment { * @throws If there is an error sending the funds. */ getFunds = async (walletAddress: string, amount = '0.001') => { + const privateKey = await this.getAvailablePrivateKey(); + try { - const privateKey = await this.getAvailablePrivateKey(); const provider = new ethers.providers.JsonRpcBatchProvider(this.rpc); const wallet = new ethers.Wallet(privateKey.privateKey, provider); @@ -460,6 +460,9 @@ export class TinnyEnvironment { await tx.wait(); } catch (e) { throw new Error(`Failed to send funds to ${walletAddress}: ${e}`); + } finally { + // @ts-expect-error We don't have a user, but this works + this.releasePrivateKeyFromUser({ privateKey }); } }; From d73b7b429bcdcd02b908bd3b69f2eb17152f70a6 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Tue, 8 Oct 2024 23:59:45 +0100 Subject: [PATCH 13/53] feat(wrapped-keys-lit-actions): LIT-3920 - Add batchGenerateEncryptedKeys LIT action - Supports generating `n` keys, with the option for each generated key to be returned with a signed message that can be provided by the caller (can be provided per key) --- .../esbuild.config.js | 1 + .../wrapped-keys-lit-actions/src/index.ts | 12 +- .../lib/common/batchGenerateEncryptedKeys.js | 159 ++++++++++++++++++ .../lib/ethereum/internal/signTransaction.js | 3 - .../lib/solana/internal/signTransaction.js | 3 - 5 files changed, 171 insertions(+), 7 deletions(-) create mode 100644 packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js diff --git a/packages/wrapped-keys-lit-actions/esbuild.config.js b/packages/wrapped-keys-lit-actions/esbuild.config.js index 6741edee93..7b0ba54732 100644 --- a/packages/wrapped-keys-lit-actions/esbuild.config.js +++ b/packages/wrapped-keys-lit-actions/esbuild.config.js @@ -57,6 +57,7 @@ module.exports = { './src/lib/ethereum/signMessageWithEncryptedEthereumKey.js', './src/lib/ethereum/generateEncryptedEthereumPrivateKey.js', './src/lib/common/exportPrivateKey.js', + './src/lib/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 f1960f5882..8ca684ec8c 100644 --- a/packages/wrapped-keys-lit-actions/src/index.ts +++ b/packages/wrapped-keys-lit-actions/src/index.ts @@ -1,3 +1,4 @@ +import * as batchGenerateEncryptedKeys from './generated/common/batchGenerateEncryptedKeys'; import * as exportPrivateKey from './generated/common/exportPrivateKey'; import * as generateEncryptedEthereumPrivateKey from './generated/ethereum/generateEncryptedEthereumPrivateKey'; import * as signMessageWithEthereumEncryptedKey from './generated/ethereum/signMessageWithEncryptedEthereumKey'; @@ -6,7 +7,10 @@ import * as generateEncryptedSolanaPrivateKey from './generated/solana/generateE import * as signMessageWithSolanaEncryptedKey from './generated/solana/signMessageWithEncryptedSolanaKey'; import * as signTransactionWithSolanaEncryptedKey from './generated/solana/signTransactionWithEncryptedSolanaKey'; -import type { LitActionCodeRepository } from '@lit-protocol/wrapped-keys'; +import type { + LitActionCodeRepository, + LitActionCodeRepositoryCommon, +} from '@lit-protocol/wrapped-keys'; const litActionRepository: LitActionCodeRepository = { signTransaction: { @@ -27,8 +31,13 @@ const litActionRepository: LitActionCodeRepository = { }, }; +const litActionRepositoryCommon: LitActionCodeRepositoryCommon = { + batchGenerateEncryptedKeys: batchGenerateEncryptedKeys.code, +}; + export { // Individual exports to allow tree-shaking and only importing the lit actions you need + batchGenerateEncryptedKeys, exportPrivateKey, generateEncryptedEthereumPrivateKey, signMessageWithEthereumEncryptedKey, @@ -39,4 +48,5 @@ export { // Full export to bundle all lit actions litActionRepository, + litActionRepositoryCommon, }; diff --git a/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js b/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js new file mode 100644 index 0000000000..f94e3d7954 --- /dev/null +++ b/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js @@ -0,0 +1,159 @@ +const { encryptPrivateKey } = require('./internal/encryptKey'); +const { + generateEthereumPrivateKey, +} = require('../ethereum/internal/generatePrivateKey'); +const { signMessageEthereumKey } = require('../ethereum/internal/signMessage'); +const { + generateSolanaPrivateKey, +} = require('../solana/internal/generatePrivateKey'); +const { signMessageSolanaKey } = require('../solana/internal/signMessage'); + +/* global accessControlConditions, actions, Lit*/ + +async function processEthereumAction(action, ndx) { + const { network, generateKeyParams } = action; + const messageToSign = action.signMessageParams?.messageToSign; + + const ethereumKey = generateEthereumPrivateKey(); + + const [generatedPrivateKey, messageSignature] = await Promise.all([ + Lit.Actions.runOnce( + { waitForResponse: true, name: `encryptEthereumPrivateKey_${ndx}` }, + async () => + JSON.stringify( + await encryptPrivateKey({ + accessControlConditions, + publicKey: ethereumKey.publicKey, + privateKey: ethereumKey.privateKey, + }) + ) + ), + messageToSign + ? signMessageEthereumKey({ + messageToSign: messageToSign, + privateKey: ethereumKey.privateKey, + }) + : Promise.resolve(), + ]); + + return { + network, + generatedPrivateKey: { + ...JSON.parse(generatedPrivateKey), + memo: generateKeyParams.memo, + }, + ...(messageSignature + ? { signedMessage: { signature: messageSignature } } + : {}), + }; +} + +async function processSolanaAction(action, ndx) { + const { network, generateKeyParams } = action; + + const messageToSign = action.signMessageParams?.messageToSign; + + const solanaKey = generateSolanaPrivateKey(); + + const [generatedPrivateKey, messageSignature] = await Promise.all([ + Lit.Actions.runOnce( + { waitForResponse: true, name: `encryptSolanaPrivateKey_${ndx}` }, + async () => + JSON.stringify( + await encryptPrivateKey({ + accessControlConditions, + publicKey: solanaKey.publicKey, + privateKey: solanaKey.privateKey, + }) + ) + ), + messageToSign + ? signMessageSolanaKey({ + messageToSign: messageToSign, + privateKey: solanaKey.privateKey, + }) + : Promise.resolve(), + ]); + + return { + network, + generatedPrivateKey: { + ...JSON.parse(generatedPrivateKey), + memo: generateKeyParams.memo, + }, + ...(messageSignature + ? { signedMessage: { signature: messageSignature } } + : {}), + }; +} + +async function processActions(actions) { + return Promise.all( + actions.map(async (action, ndx) => { + const { network } = action; + + if (network === 'evm') { + return await processEthereumAction(action, ndx); + } else if (network === 'solana') { + return await processSolanaAction(action, ndx); + } else { + // Just in case :tm: + throw new Error(`Invalid network for action[${ndx}]: ${network}`); + } + }) + ); +} + +function validateParams(actions) { + if (!actions) { + throw new Error('Missing required field: actions'); + } + + if (!actions.length) { + throw new Error('No actions provided (empty array?)'); + } + + actions.forEach((action, ndx) => { + if (!['evm', 'solana'].includes(action.network)) { + throw new Error( + `Invalid field: actions[${ndx}].network: ${action.network}` + ); + } + + if (!action.generateKeyParams) { + throw new Error( + `Missing required field: actions[${ndx}].generateKeyParams` + ); + } + + if (!action.generateKeyParams?.memo) { + throw new Error( + `Missing required field: actions[${ndx}].generateKeyParams.memo` + ); + } + + if (action.signMessageParams && !action.signMessageParams?.messageToSign) { + throw new Error( + `Missing required field: actions[${ndx}].signMessageParams.messageToSign` + ); + } + }); +} + +(async () => { + try { + validateParams(actions); + + const batchGeneratePrivateKeysActionResult = await processActions(actions); + + Lit.Actions.setResponse({ + response: JSON.stringify(batchGeneratePrivateKeysActionResult), + }); + + // 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 + err.stack}` }); + } +})(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransaction.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransaction.js index 232b294073..103144525f 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransaction.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransaction.js @@ -1,6 +1,3 @@ -import { getDecryptedKey } from '../../common/internal/getDecryptedKey'; -import { removeSaltFromDecryptedKey } from '../../utils'; - /* global ethers, Lit */ function getValidatedUnsignedTx(unsignedTransaction) { diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransaction.js b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransaction.js index 305e1f9451..0c487ff124 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransaction.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransaction.js @@ -1,6 +1,3 @@ -import { getDecryptedKey } from '../../common/internal/getDecryptedKey'; -import { removeSaltFromDecryptedKey } from '../../utils'; - const { clusterApiUrl, Connection, From 4f632f711edcf55ae87584c1c54ba6609fa2b888 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 00:07:59 +0100 Subject: [PATCH 14/53] test(wrapped-keys): LIT-3920 - Define testBatchGenerateEncryptedKeys wrapped-keys client test - Added utility functions to allow LIT action repository to be populated entirely with local code instead of using CIDs - Use _local_ LIT action source code for running wrapped-keys tests - When we migrate to Jest we can make this the tests against both CIDs and local source code in parallel --- local-tests/test.ts | 7 ++ .../testBatchGenerateEncryptedKeys.ts | 75 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 local-tests/tests/wrapped-keys/testBatchGenerateEncryptedKeys.ts diff --git a/local-tests/test.ts b/local-tests/test.ts index 1c43482116..241b8b5c99 100644 --- a/local-tests/test.ts +++ b/local-tests/test.ts @@ -104,6 +104,12 @@ import { testFailImportWrappedKeysWithExpiredSessionSig } from './tests/wrapped- import { testExportWrappedKey } from './tests/wrapped-keys/testExportWrappedKey'; import { testSignMessageWithSolanaEncryptedKey } from './tests/wrapped-keys/testSignMessageWithSolanaEncryptedKey'; import { testSignTransactionWithSolanaEncryptedKey } from './tests/wrapped-keys/testSignTransactionWithSolanaEncryptedKey'; +import { testBatchGenerateEncryptedKeys } from './tests/wrapped-keys/testBatchGenerateEncryptedKeys'; + +import { setLitActionsCodeToLocal } from './tests/wrapped-keys/util'; + +// Use the current LIT action code to test against +setLitActionsCodeToLocal(); (async () => { console.log('[𐬺🧪 Tinny𐬺] Running tests...'); @@ -118,6 +124,7 @@ import { testSignTransactionWithSolanaEncryptedKey } from './tests/wrapped-keys/ // --filter=WrappedKey const wrappedKeysTests = { // -- valid cases + testBatchGenerateEncryptedKeys, testEthereumSignMessageGeneratedKey, testEthereumBroadcastTransactionGeneratedKey, testEthereumSignMessageWrappedKey, diff --git a/local-tests/tests/wrapped-keys/testBatchGenerateEncryptedKeys.ts b/local-tests/tests/wrapped-keys/testBatchGenerateEncryptedKeys.ts new file mode 100644 index 0000000000..e65709dd85 --- /dev/null +++ b/local-tests/tests/wrapped-keys/testBatchGenerateEncryptedKeys.ts @@ -0,0 +1,75 @@ +import { log } from '@lit-protocol/misc'; +import { TinnyEnvironment } from 'local-tests/setup/tinny-environment'; +import { api } from '@lit-protocol/wrapped-keys'; +import { getPkpSessionSigs } from 'local-tests/setup/session-sigs/get-pkp-session-sigs'; + +const { batchGenerateEncryptedKeys } = api; + +/** + * Test Commands: + * ✅ NETWORK=cayenne yarn test:local --filter=testSignMessageWithSolanaEncryptedKey + * ✅ NETWORK=manzano yarn test:local --filter=testSignMessageWithSolanaEncryptedKey + * ✅ NETWORK=localchain yarn test:local --filter=testSignMessageWithSolanaEncryptedKey + */ +export const testBatchGenerateEncryptedKeys = async ( + devEnv: TinnyEnvironment +) => { + const alice = await devEnv.createRandomPerson(); + + try { + const pkpSessionSigsSigning = await getPkpSessionSigs( + devEnv, + alice, + null, + new Date(Date.now() + 1000 * 60 * 10).toISOString() + ); // 10 mins expiry + + const results = await batchGenerateEncryptedKeys({ + pkpSessionSigs: pkpSessionSigsSigning, + actions: [ + { + network: 'evm', + signMessageParams: { messageToSign: 'This is a test evm message' }, + generateKeyParams: { memo: 'Test evm key' }, + }, + { + network: 'solana', + signMessageParams: { messageToSign: 'This is a test solana message' }, + generateKeyParams: { memo: 'Test solana key' }, + }, + ], + litNodeClient: devEnv.litNodeClient, + }); + + if (results.length !== 2) { + throw new Error( + `Incorrect # of results; expected 2, got ${results.length}` + ); + } + + if ( + results[0].generatedPrivateKey.memo !== 'Test evm key' || + results[1].generatedPrivateKey.memo !== 'Test solana key' + ) { + throw new Error( + 'Results not in order sent; expected evm as first result, solana as second' + ); + } + + if ( + !results[0].signedMessage.signature || + !results[1].signedMessage.signature + ) { + throw new Error('Missing message signature in response'); + } + + console.log('results', results); + + log('✅ testBatchGenerateEncryptedKeys'); + } catch (err) { + console.log(err.message, err, err.stack); + throw err; + } finally { + devEnv.releasePrivateKeyFromUser(alice); + } +}; From e830162ca32b20efaef28bd08250dd209185938a Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 00:10:14 +0100 Subject: [PATCH 15/53] chore(wrapped-keys): LIT-3920 - Define new to hold LIT actions that are not network-specific litActionCodeRepositoryCommon and LIT_ACTION_CID_REPOSITORY_COMMON --- .../lib/lit-actions-client/code-repository.ts | 66 +++++++++++++++++-- .../src/lib/lit-actions-client/constants.ts | 8 ++- .../src/lib/lit-actions-client/types.ts | 11 ++++ .../src/lib/lit-actions-client/utils.ts | 43 +++++++++++- 4 files changed, 118 insertions(+), 10 deletions(-) diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/code-repository.ts b/packages/wrapped-keys/src/lib/lit-actions-client/code-repository.ts index 3cd7b1a21e..54363e47a2 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/code-repository.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/code-repository.ts @@ -1,5 +1,7 @@ import { LitActionCodeRepository, + LitActionCodeRepositoryCommon, + LitActionCodeRepositoryCommonInput, LitActionCodeRepositoryEntry, LitActionCodeRepositoryInput, LitActionType, @@ -52,10 +54,10 @@ function assertIsLitActionRepositoryEntry( if ( typeof entry !== 'object' || !entry || - ('evm' in entry && - typeof (entry as LitActionCodeRepositoryEntry).evm !== 'string') || - ('solana' in entry && - typeof (entry as LitActionCodeRepositoryEntry).solana !== 'string') || + // @ts-expect-error assert function + ('evm' in entry && typeof entry.evm !== 'string') || + // @ts-expect-error assert function + ('solana' in entry && typeof entry.solana !== 'string') || Object.keys(entry).some((key) => !['evm', 'solana'].includes(key)) ) { throw new Error( @@ -80,4 +82,58 @@ function setLitActionsCode(repository: LitActionCodeRepositoryInput) { } } -export { litActionCodeRepository, setLitActionsCode }; +/** + * A repository for managing Lit Actions related to blockchain operations. + * Contains actions that are designed to be used for multiple networks + * @type {LitActionCodeRepositoryCommon} + */ +const litActionCodeRepositoryCommon: LitActionCodeRepositoryCommon = { + batchGenerateEncryptedKeys: '', +}; + +function assertIsLitActionKeyCommon( + key: string +): asserts key is keyof LitActionCodeRepositoryCommon { + if (!(key in litActionCodeRepositoryCommon)) { + throw new Error( + `Invalid key: ${key}; must be one of ${Object.keys( + litActionCodeRepositoryCommon + ).join(',')}` + ); + } +} + +/** + * Type Guard for LitActionCodeRepositoryEntry + */ +function assertIsLitActionRepositoryEntryCommon( + entry: unknown +): asserts entry is LitActionCodeRepositoryEntry { + if (typeof entry !== 'string') { + throw new Error( + `Invalid LitActionRepositoryCommon entry: ${JSON.stringify(entry)}` + ); + } +} + +/** + * Updates the litActionCodeRepository with the provided entries. + * @param { LitActionCodeRepositoryCommonInput } repository - user provided repository to set + */ +function setLitActionsCodeCommon( + repository: LitActionCodeRepositoryCommonInput +) { + for (const [actionType, actionCode] of Object.entries(repository)) { + assertIsLitActionKeyCommon(actionType); + assertIsLitActionRepositoryEntryCommon(actionCode); + + litActionCodeRepositoryCommon[actionType] = actionCode; + } +} + +export { + litActionCodeRepository, + setLitActionsCode, + litActionCodeRepositoryCommon, + setLitActionsCodeCommon, +}; diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/constants.ts b/packages/wrapped-keys/src/lib/lit-actions-client/constants.ts index a0f4c08e76..328daa46b6 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/constants.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/constants.ts @@ -1,4 +1,4 @@ -import { LitCidRepository } from './types'; +import { LitCidRepository, LitCidRepositoryCommon } from './types'; const LIT_ACTION_CID_REPOSITORY: LitCidRepository = Object.freeze({ signTransaction: Object.freeze({ @@ -19,4 +19,8 @@ const LIT_ACTION_CID_REPOSITORY: LitCidRepository = Object.freeze({ }), }); -export { LIT_ACTION_CID_REPOSITORY }; +const LIT_ACTION_CID_REPOSITORY_COMMON: LitCidRepositoryCommon = Object.freeze({ + batchGenerateEncryptedKeys: '', +}); + +export { LIT_ACTION_CID_REPOSITORY, LIT_ACTION_CID_REPOSITORY_COMMON }; diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/types.ts b/packages/wrapped-keys/src/lib/lit-actions-client/types.ts index 85510087fd..bda28ecd57 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/types.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/types.ts @@ -6,12 +6,18 @@ export type LitActionType = | 'generateEncryptedKey' | 'exportPrivateKey'; +export type LitActionTypeCommon = 'batchGenerateEncryptedKeys'; + export type LitCidRepositoryEntry = Readonly>; export type LitCidRepository = Readonly< Record >; +export type LitCidRepositoryCommon = Readonly< + Record +>; + /** * A type that represents an entry in a Lit Action Code repository. * @@ -36,3 +42,8 @@ export type LitActionCodeRepository = Readonly< export type LitActionCodeRepositoryInput = Partial< Record >; + +export type LitActionCodeRepositoryCommon = Record; +export type LitActionCodeRepositoryCommonInput = Partial< + Record +>; diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/utils.ts b/packages/wrapped-keys/src/lib/lit-actions-client/utils.ts index 14d505d855..abd7301b97 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/utils.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/utils.ts @@ -1,8 +1,14 @@ import { ExecuteJsResponse } from '@lit-protocol/types'; -import { litActionCodeRepository } from './code-repository'; -import { LIT_ACTION_CID_REPOSITORY } from './constants'; -import { LitActionType } from './types'; +import { + litActionCodeRepository, + litActionCodeRepositoryCommon, +} from './code-repository'; +import { + LIT_ACTION_CID_REPOSITORY, + LIT_ACTION_CID_REPOSITORY_COMMON, +} from './constants'; +import { LitActionType, LitActionTypeCommon } from './types'; import { Network } from '../types'; /** @@ -77,6 +83,7 @@ function assertNetworkIsValid(network: any): asserts network is Network { /** * Fetch the Lit action code or its IPFS CID for a given network and action type. + * @private * * @param {Network} network The network to get the code or CID for. * @param {LitActionType} actionType The type of action to get the code or CID for. @@ -94,3 +101,33 @@ export function getLitActionCodeOrCid( } return { litActionIpfsCid: getLitActionCid(network, actionType) }; } + +/** + * Fetch the Lit action code or its IPFS CID for a given network and action type. + * @private + * @param {LitActionTypeCommon} actionType The type of action to get the code or CID for. + * @returns {{ litActionCode?: string, litActionIpfsCid?: string }} The Lit action code or its IPFS CID. + */ +export function getLitActionCodeOrCidCommon(actionType: LitActionTypeCommon): { + litActionCode?: string; + litActionIpfsCid?: string; +} { + // Default state is that litActionCode will be falsy, unless someone has injected to it using `setLitActionsCode(); + const litActionCode = getLitActionCodeCommon(actionType); + + if (litActionCode) { + return { litActionCode }; + } + return { litActionIpfsCid: getLitActionCidCommon(actionType) }; +} + +export function getLitActionCidCommon(actionType: LitActionTypeCommon) { + return LIT_ACTION_CID_REPOSITORY_COMMON[actionType]; +} + +export function getLitActionCodeCommon( + actionType: LitActionTypeCommon +): string { + // No fuzzy validation needed here, because `setLitActionsCodeCommon()` validates its input + return litActionCodeRepositoryCommon[actionType]; +} From f7de6b6170cee8dad406b05aa2f6b67e52236c6b Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 00:11:39 +0100 Subject: [PATCH 16/53] chore(wrapped-keys): LIT-3920 - run eslint --fix --- .../src/lib/lit-actions-client/export-private-key.ts | 2 +- .../wrapped-keys/src/lib/lit-actions-client/generate-key.ts | 3 ++- .../wrapped-keys/src/lib/lit-actions-client/sign-message.ts | 2 +- .../src/lib/lit-actions-client/sign-transaction.ts | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/export-private-key.ts b/packages/wrapped-keys/src/lib/lit-actions-client/export-private-key.ts index 59f4c0fe57..8dd6ffb7ac 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/export-private-key.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/export-private-key.ts @@ -1,8 +1,8 @@ +import { GLOBAL_OVERWRITE_IPFS_CODE_BY_NETWORK } from '@lit-protocol/constants'; import { AccessControlConditions } from '@lit-protocol/types'; import { postLitActionValidation } from './utils'; import { ExportPrivateKeyParams, StoredKeyData } from '../types'; -import { GLOBAL_OVERWRITE_IPFS_CODE_BY_NETWORK } from '@lit-protocol/constants'; interface SignMessageWithLitActionParams extends ExportPrivateKeyParams { accessControlConditions: AccessControlConditions; diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/generate-key.ts b/packages/wrapped-keys/src/lib/lit-actions-client/generate-key.ts index 5ecceaa5fa..02b36c767e 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/generate-key.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/generate-key.ts @@ -1,7 +1,8 @@ +import { GLOBAL_OVERWRITE_IPFS_CODE_BY_NETWORK } from '@lit-protocol/constants'; import { AccessControlConditions } from '@lit-protocol/types'; + import { postLitActionValidation } from './utils'; import { GeneratePrivateKeyParams } from '../types'; -import { GLOBAL_OVERWRITE_IPFS_CODE_BY_NETWORK } from '@lit-protocol/constants'; interface GeneratePrivateKeyLitActionParams extends GeneratePrivateKeyParams { pkpAddress: string; diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/sign-message.ts b/packages/wrapped-keys/src/lib/lit-actions-client/sign-message.ts index 330f90ed70..f5aec3f126 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/sign-message.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/sign-message.ts @@ -1,8 +1,8 @@ +import { GLOBAL_OVERWRITE_IPFS_CODE_BY_NETWORK } from '@lit-protocol/constants'; import { AccessControlConditions } from '@lit-protocol/types'; import { postLitActionValidation } from './utils'; import { SignMessageWithEncryptedKeyParams, StoredKeyData } from '../types'; -import { GLOBAL_OVERWRITE_IPFS_CODE_BY_NETWORK } from '@lit-protocol/constants'; interface SignMessageWithLitActionParams extends SignMessageWithEncryptedKeyParams { diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/sign-transaction.ts b/packages/wrapped-keys/src/lib/lit-actions-client/sign-transaction.ts index 6e3cee6842..5dde9e0b37 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/sign-transaction.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/sign-transaction.ts @@ -1,3 +1,4 @@ +import { GLOBAL_OVERWRITE_IPFS_CODE_BY_NETWORK } from '@lit-protocol/constants'; import { AccessControlConditions, ILitNodeClient, @@ -10,7 +11,6 @@ import { SerializedTransaction, StoredKeyData, } from '../types'; -import { GLOBAL_OVERWRITE_IPFS_CODE_BY_NETWORK } from '@lit-protocol/constants'; interface SignTransactionWithLitActionParams { litNodeClient: ILitNodeClient; From d5da0e3f6d9a7a9195b2acba4db4fa1ad5958897 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 00:19:46 +0100 Subject: [PATCH 17/53] types(wrapped-keys): LIT-3920 - Add initial types to facilitate batchGeneratePrivateKeys functionality --- packages/wrapped-keys/src/lib/types.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/wrapped-keys/src/lib/types.ts b/packages/wrapped-keys/src/lib/types.ts index abfb640998..69b871160d 100644 --- a/packages/wrapped-keys/src/lib/types.ts +++ b/packages/wrapped-keys/src/lib/types.ts @@ -134,6 +134,32 @@ export interface ExportPrivateKeyResult { id: string; } +/** @typedef GeneratePrivateKeyAction + * @extends ApiParamsSupportedNetworks + */ +export type GeneratePrivateKeyAction = ApiParamsSupportedNetworks & { + generateKeyParams: { memo: string }; + signMessageParams?: { messageToSign: string | Uint8Array }; +}; + +/** @typedef BatchGeneratePrivateKeysParams + * @extends BaseApiParams + */ +export type BatchGeneratePrivateKeysParams = BaseApiParams & { + actions: GeneratePrivateKeyAction[]; +}; + +/** @typedef GeneratePrivateKeyAction + * @extends ApiParamsSupportedNetworks + */ +export interface BatchGeneratePrivateKeysActionResult { + generatedPrivateKey: GeneratePrivateKeyResult & { memo: string }; + signedMessage?: { signature: string }; +} + +export type BatchGeneratePrivateKeysResult = + BatchGeneratePrivateKeysActionResult[]; + /** @typedef GeneratePrivateKeyParams * @extends BaseApiParams * @property {Network} network The network for which the private key needs to be generated; keys are generated differently for different networks From 610d2ef829ed7d6d316f203ee5f75084d2144c76 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 00:20:29 +0100 Subject: [PATCH 18/53] feat(wrapped-keys): LIT-3920 - Add batchGenerateKeysWithLitAction to lit-actions-client --- .../lit-actions-client/batch-generate-keys.ts | 55 +++++++++++++++++++ .../src/lib/lit-actions-client/index.ts | 4 +- 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 packages/wrapped-keys/src/lib/lit-actions-client/batch-generate-keys.ts diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/batch-generate-keys.ts b/packages/wrapped-keys/src/lib/lit-actions-client/batch-generate-keys.ts new file mode 100644 index 0000000000..84cf2ff04d --- /dev/null +++ b/packages/wrapped-keys/src/lib/lit-actions-client/batch-generate-keys.ts @@ -0,0 +1,55 @@ +import { GLOBAL_OVERWRITE_IPFS_CODE_BY_NETWORK } from '@lit-protocol/constants'; +import { AccessControlConditions } from '@lit-protocol/types'; + +import { postLitActionValidation } from './utils'; +import { BatchGeneratePrivateKeysParams, Network } from '../types'; + +interface BatchGeneratePrivateKeysWithLitActionParams + extends BatchGeneratePrivateKeysParams { + accessControlConditions: AccessControlConditions; + litActionIpfsCid?: string; + litActionCode?: string; +} + +interface GeneratePrivateKeyLitActionResult { + ciphertext: string; + dataToEncryptHash: string; + publicKey: string; + memo: string; +} + +interface BatchGeneratePrivateKeysWithLitActionResult { + network: Network; + signedMessage?: { signature: string }; + generatedPrivateKey: GeneratePrivateKeyLitActionResult; +} + +export async function batchGenerateKeysWithLitAction( + args: BatchGeneratePrivateKeysWithLitActionParams +): Promise { + const { + accessControlConditions, + litNodeClient, + actions, + pkpSessionSigs, + litActionIpfsCid, + litActionCode, + } = args; + + const result = await litNodeClient.executeJs({ + sessionSigs: pkpSessionSigs, + ipfsId: litActionIpfsCid, + code: litActionCode, + jsParams: { + actions, + accessControlConditions, + }, + ipfsOptions: { + overwriteCode: + GLOBAL_OVERWRITE_IPFS_CODE_BY_NETWORK[litNodeClient.config.litNetwork], + }, + }); + + const response = postLitActionValidation(result); + return JSON.parse(response); +} diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/index.ts b/packages/wrapped-keys/src/lib/lit-actions-client/index.ts index a383d8a0e9..e523e48e62 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/index.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/index.ts @@ -1,11 +1,13 @@ +import { batchGenerateKeysWithLitAction } from './batch-generate-keys'; +import { exportPrivateKeyWithLitAction } from './export-private-key'; import { generateKeyWithLitAction } from './generate-key'; import { signMessageWithLitAction } from './sign-message'; import { signTransactionWithLitAction } from './sign-transaction'; -import { exportPrivateKeyWithLitAction } from './export-private-key'; export { generateKeyWithLitAction, signTransactionWithLitAction, signMessageWithLitAction, exportPrivateKeyWithLitAction, + batchGenerateKeysWithLitAction, }; From 4d34fb1369365629197fc7f5bf25a1ece9ffd0cb Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 00:21:54 +0100 Subject: [PATCH 19/53] refactor(wrapped-keys): LIT-3920 - Extract getKeyTypeFromNetwork into `utils` for use in both generate codepaths --- .../src/lib/api/generate-private-key.ts | 18 ++---------------- packages/wrapped-keys/src/lib/api/utils.ts | 12 ++++++++++++ 2 files changed, 14 insertions(+), 16 deletions(-) create mode 100644 packages/wrapped-keys/src/lib/api/utils.ts diff --git a/packages/wrapped-keys/src/lib/api/generate-private-key.ts b/packages/wrapped-keys/src/lib/api/generate-private-key.ts index cb898c742f..2c8216c947 100644 --- a/packages/wrapped-keys/src/lib/api/generate-private-key.ts +++ b/packages/wrapped-keys/src/lib/api/generate-private-key.ts @@ -1,28 +1,14 @@ -import { NETWORK_EVM, NETWORK_SOLANA } from '../constants'; +import { getKeyTypeFromNetwork } from './utils'; import { generateKeyWithLitAction } from '../lit-actions-client'; import { getLitActionCodeOrCid } from '../lit-actions-client/utils'; import { storePrivateKey } from '../service-client'; -import { - GeneratePrivateKeyParams, - GeneratePrivateKeyResult, - KeyType, - Network, -} from '../types'; +import { GeneratePrivateKeyParams, GeneratePrivateKeyResult } from '../types'; import { getFirstSessionSig, getPkpAccessControlCondition, getPkpAddressFromSessionSig, } from '../utils'; -function getKeyTypeFromNetwork(network: Network): KeyType { - if (network === NETWORK_EVM) { - return 'K256'; - } else if (network === NETWORK_SOLANA) { - return 'ed25519'; - } else { - throw new Error('Network not implemented in generate-private-key'); - } -} /** * Generates a random private key inside a Lit Action, and persists the key and its metadata to the wrapped keys service. * Returns the public key of the random private key, and the PKP address that it was associated with. diff --git a/packages/wrapped-keys/src/lib/api/utils.ts b/packages/wrapped-keys/src/lib/api/utils.ts new file mode 100644 index 0000000000..f064062a63 --- /dev/null +++ b/packages/wrapped-keys/src/lib/api/utils.ts @@ -0,0 +1,12 @@ +import { NETWORK_EVM, NETWORK_SOLANA } from '../constants'; +import { KeyType, Network } from '../types'; + +export function getKeyTypeFromNetwork(network: Network): KeyType { + if (network === NETWORK_EVM) { + return 'K256'; + } else if (network === NETWORK_SOLANA) { + return 'ed25519'; + } else { + throw new Error(`Network not implemented ${network}`); + } +} From ca76c7bfb76dd3a75dcc3791f2ac0651b803aad7 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 00:23:27 +0100 Subject: [PATCH 20/53] feat(wrapped-keys): LIT-3920 - Add batchGeneratePrivateKeys to client api interface, and export setLitActionsCodeCommon to allow injection of LA code for the new `common` code repository --- packages/core/src/lib/lit-core.ts | 9 +-- packages/wrapped-keys/src/index.ts | 10 ++- .../lib/api/batch-generate-private-keys.ts | 73 +++++++++++++++++++ packages/wrapped-keys/src/lib/api/index.ts | 2 + 4 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 packages/wrapped-keys/src/lib/api/batch-generate-private-keys.ts diff --git a/packages/core/src/lib/lit-core.ts b/packages/core/src/lib/lit-core.ts index 074fdc107d..a7188a9305 100644 --- a/packages/core/src/lib/lit-core.ts +++ b/packages/core/src/lib/lit-core.ts @@ -1022,11 +1022,10 @@ export class LitCore { delete data.sessionSigs; } - logWithRequestId( - requestId, - `sendCommandToNode with url ${url} and data`, - data - ); + logWithRequestId(requestId, `sendCommandToNode with url ${url} and data`, { + jsParams: JSON.stringify(data.jsParams), + codeLength: data.code?.length, + }); const req: RequestInit = { method: 'POST', diff --git a/packages/wrapped-keys/src/index.ts b/packages/wrapped-keys/src/index.ts index 4149ce1d66..ea0b95d524 100644 --- a/packages/wrapped-keys/src/index.ts +++ b/packages/wrapped-keys/src/index.ts @@ -7,6 +7,7 @@ import { signTransactionWithEncryptedKey, storeEncryptedKey, listEncryptedKeyMetadata, + batchGeneratePrivateKeys, } from './lib/api'; import { CHAIN_ETHEREUM, @@ -16,9 +17,13 @@ import { KEYTYPE_K256, KEYTYPE_ED25519, } from './lib/constants'; -import { setLitActionsCode } from './lib/lit-actions-client/code-repository'; +import { + setLitActionsCode, + setLitActionsCodeCommon, +} from './lib/lit-actions-client/code-repository'; import { LitActionCodeRepository, + LitActionCodeRepositoryCommon, LitActionCodeRepositoryEntry, } from './lib/lit-actions-client/types'; @@ -65,10 +70,12 @@ export const api = { signMessageWithEncryptedKey, signTransactionWithEncryptedKey, storeEncryptedKey, + batchGeneratePrivateKeys, }; export const config = { setLitActionsCode, + setLitActionsCodeCommon, }; export { @@ -84,6 +91,7 @@ export { ImportPrivateKeyResult, ListEncryptedKeyMetadataParams, LitActionCodeRepository, + LitActionCodeRepositoryCommon, LitActionCodeRepositoryEntry, SerializedTransaction, SignTransactionParams, diff --git a/packages/wrapped-keys/src/lib/api/batch-generate-private-keys.ts b/packages/wrapped-keys/src/lib/api/batch-generate-private-keys.ts new file mode 100644 index 0000000000..af0dafaeeb --- /dev/null +++ b/packages/wrapped-keys/src/lib/api/batch-generate-private-keys.ts @@ -0,0 +1,73 @@ +import { getKeyTypeFromNetwork } from './utils'; +import { batchGenerateKeysWithLitAction } from '../lit-actions-client'; +import { getLitActionCodeOrCidCommon } from '../lit-actions-client/utils'; +import { storePrivateKey } from '../service-client'; +import { + BatchGeneratePrivateKeysActionResult, + BatchGeneratePrivateKeysParams, + BatchGeneratePrivateKeysResult, +} from '../types'; +import { + getFirstSessionSig, + getPkpAccessControlCondition, + getPkpAddressFromSessionSig, +} from '../utils'; + +/** + * TODO: Document batch behaviour + * @param { BatchGeneratePrivateKeysParams } params Parameters to use for generating keys and optionally signing messages + * + * @returns { Promise } - The generated keys and, optionally, signed messages + */ +export async function batchGeneratePrivateKeys( + params: BatchGeneratePrivateKeysParams +): Promise { + const { pkpSessionSigs, litNodeClient } = params; + + const sessionSig = getFirstSessionSig(pkpSessionSigs); + const pkpAddress = getPkpAddressFromSessionSig(sessionSig); + + const allowPkpAddressToDecrypt = getPkpAccessControlCondition(pkpAddress); + + const { litActionCode, litActionIpfsCid } = getLitActionCodeOrCidCommon( + 'batchGenerateEncryptedKeys' + ); + + const actionResults = await batchGenerateKeysWithLitAction({ + ...params, + litActionIpfsCid: litActionCode ? undefined : litActionIpfsCid, + litActionCode: litActionCode ? litActionCode : undefined, + accessControlConditions: [allowPkpAddressToDecrypt], + pkpSessionSigs, + }); + + return Promise.all( + actionResults.map( + async (result): Promise => { + const { generatedPrivateKey, network } = result; + + const signature = result.signedMessage?.signature; + + const { id } = await storePrivateKey({ + sessionSig, + storedKeyMetadata: { + ...generatedPrivateKey, + keyType: getKeyTypeFromNetwork(network), + pkpAddress, + }, + litNetwork: litNodeClient.config.litNetwork, + }); + + return { + ...(signature ? { signedMessage: { signature } } : {}), + generatedPrivateKey: { + memo: generatedPrivateKey.memo, + id: id, + generatedPublicKey: generatedPrivateKey.publicKey, + pkpAddress, + }, + }; + } + ) + ); +} diff --git a/packages/wrapped-keys/src/lib/api/index.ts b/packages/wrapped-keys/src/lib/api/index.ts index 99b33eda35..d13292a017 100644 --- a/packages/wrapped-keys/src/lib/api/index.ts +++ b/packages/wrapped-keys/src/lib/api/index.ts @@ -1,3 +1,4 @@ +import { batchGeneratePrivateKeys } from './batch-generate-private-keys'; import { exportPrivateKey } from './export-private-key'; import { generatePrivateKey } from './generate-private-key'; import { getEncryptedKey } from './get-encrypted-key'; @@ -16,4 +17,5 @@ export { signMessageWithEncryptedKey, storeEncryptedKey, getEncryptedKey, + batchGeneratePrivateKeys, }; From 13269e7eb27cc09ff76d1e72082f04a3c58231e2 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 00:26:27 +0100 Subject: [PATCH 21/53] test(wrapped-keys-lit-actions): LIT-3920 - Rename to testBatchGeneratePrivateKeys to match API export - Export setLitActionsCodeToLocal and resetLitActionsCode methods from wrapped-keys utils to allow tests to run on in-repo LA code --- local-tests/test.ts | 4 +- ...eys.ts => testBatchGeneratePrivateKeys.ts} | 6 +-- local-tests/tests/wrapped-keys/util.ts | 45 ++++++++++++++++++- 3 files changed, 49 insertions(+), 6 deletions(-) rename local-tests/tests/wrapped-keys/{testBatchGenerateEncryptedKeys.ts => testBatchGeneratePrivateKeys.ts} (93%) diff --git a/local-tests/test.ts b/local-tests/test.ts index 241b8b5c99..751782294a 100644 --- a/local-tests/test.ts +++ b/local-tests/test.ts @@ -104,7 +104,7 @@ import { testFailImportWrappedKeysWithExpiredSessionSig } from './tests/wrapped- import { testExportWrappedKey } from './tests/wrapped-keys/testExportWrappedKey'; import { testSignMessageWithSolanaEncryptedKey } from './tests/wrapped-keys/testSignMessageWithSolanaEncryptedKey'; import { testSignTransactionWithSolanaEncryptedKey } from './tests/wrapped-keys/testSignTransactionWithSolanaEncryptedKey'; -import { testBatchGenerateEncryptedKeys } from './tests/wrapped-keys/testBatchGenerateEncryptedKeys'; +import { testBatchGeneratePrivateKeys } from './tests/wrapped-keys/testBatchGeneratePrivateKeys'; import { setLitActionsCodeToLocal } from './tests/wrapped-keys/util'; @@ -124,7 +124,7 @@ setLitActionsCodeToLocal(); // --filter=WrappedKey const wrappedKeysTests = { // -- valid cases - testBatchGenerateEncryptedKeys, + testBatchGeneratePrivateKeys, testEthereumSignMessageGeneratedKey, testEthereumBroadcastTransactionGeneratedKey, testEthereumSignMessageWrappedKey, diff --git a/local-tests/tests/wrapped-keys/testBatchGenerateEncryptedKeys.ts b/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts similarity index 93% rename from local-tests/tests/wrapped-keys/testBatchGenerateEncryptedKeys.ts rename to local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts index e65709dd85..2467fa0adc 100644 --- a/local-tests/tests/wrapped-keys/testBatchGenerateEncryptedKeys.ts +++ b/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts @@ -3,7 +3,7 @@ import { TinnyEnvironment } from 'local-tests/setup/tinny-environment'; import { api } from '@lit-protocol/wrapped-keys'; import { getPkpSessionSigs } from 'local-tests/setup/session-sigs/get-pkp-session-sigs'; -const { batchGenerateEncryptedKeys } = api; +const { batchGeneratePrivateKeys } = api; /** * Test Commands: @@ -11,7 +11,7 @@ const { batchGenerateEncryptedKeys } = api; * ✅ NETWORK=manzano yarn test:local --filter=testSignMessageWithSolanaEncryptedKey * ✅ NETWORK=localchain yarn test:local --filter=testSignMessageWithSolanaEncryptedKey */ -export const testBatchGenerateEncryptedKeys = async ( +export const testBatchGeneratePrivateKeys = async ( devEnv: TinnyEnvironment ) => { const alice = await devEnv.createRandomPerson(); @@ -24,7 +24,7 @@ export const testBatchGenerateEncryptedKeys = async ( new Date(Date.now() + 1000 * 60 * 10).toISOString() ); // 10 mins expiry - const results = await batchGenerateEncryptedKeys({ + const results = await batchGeneratePrivateKeys({ pkpSessionSigs: pkpSessionSigsSigning, actions: [ { diff --git a/local-tests/tests/wrapped-keys/util.ts b/local-tests/tests/wrapped-keys/util.ts index 68bf85f8dc..4f3573fe33 100644 --- a/local-tests/tests/wrapped-keys/util.ts +++ b/local-tests/tests/wrapped-keys/util.ts @@ -1,7 +1,50 @@ import { LIT_NETWORKS_KEYS } from '@lit-protocol/types'; import { LIT_CHAINS } from '@lit-protocol/constants'; import { ethers } from 'ethers'; -import { EthereumLitTransaction } from '@lit-protocol/wrapped-keys'; +import { config } from '@lit-protocol/wrapped-keys'; +import { + litActionRepositoryCommon, + litActionRepository, +} from '@lit-protocol/wrapped-keys-lit-actions'; + +import type { + LitActionCodeRepository, + LitActionCodeRepositoryCommon, + EthereumLitTransaction, +} from '@lit-protocol/wrapped-keys'; + +const emptyLitActionRepositoryCommon: LitActionCodeRepositoryCommon = { + batchGenerateEncryptedKeys: '', +}; + +const emptyLitActionRepository: LitActionCodeRepository = { + signTransaction: { + evm: '', + solana: '', + }, + signMessage: { + evm: '', + solana: '', + }, + generateEncryptedKey: { + evm: '', + solana: '', + }, + exportPrivateKey: { + evm: '', + solana: '', + }, +}; + +export function resetLitActionsCode() { + config.setLitActionsCodeCommon(emptyLitActionRepositoryCommon); + config.setLitActionsCode(emptyLitActionRepository); +} + +export function setLitActionsCodeToLocal() { + config.setLitActionsCodeCommon(litActionRepositoryCommon); + config.setLitActionsCode(litActionRepository); +} export function getChainForNetwork(network: LIT_NETWORKS_KEYS): { chain: string; From 442dae847858d59753c94cbad09eff6e262c8ea6 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 00:28:41 +0100 Subject: [PATCH 22/53] test(wrapped-keys-lit-actions): LIT-3920 - Update assertions to handle new error string format --- ...FailEthereumSignTransactionWrappedKeyWithInvalidParam.ts | 6 +----- ...FailEthereumSignTransactionWrappedKeyWithMissingParam.ts | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/local-tests/tests/wrapped-keys/testFailEthereumSignTransactionWrappedKeyWithInvalidParam.ts b/local-tests/tests/wrapped-keys/testFailEthereumSignTransactionWrappedKeyWithInvalidParam.ts index f2e1ac8feb..8aaccca971 100644 --- a/local-tests/tests/wrapped-keys/testFailEthereumSignTransactionWrappedKeyWithInvalidParam.ts +++ b/local-tests/tests/wrapped-keys/testFailEthereumSignTransactionWrappedKeyWithInvalidParam.ts @@ -70,11 +70,7 @@ export const testFailEthereumSignTransactionWrappedKeyWithInvalidParam = async ( id, }); } catch (e: any) { - if ( - e.message.includes( - 'Error executing the Signing Lit Action: Error: When signing transaction- invalid hexlify value' - ) - ) { + if (e.message.includes('invalid hexlify value')) { console.log('✅ THIS IS EXPECTED: ', e); console.log(e.message); console.log( diff --git a/local-tests/tests/wrapped-keys/testFailEthereumSignTransactionWrappedKeyWithMissingParam.ts b/local-tests/tests/wrapped-keys/testFailEthereumSignTransactionWrappedKeyWithMissingParam.ts index c66d81c44d..f23c0d6910 100644 --- a/local-tests/tests/wrapped-keys/testFailEthereumSignTransactionWrappedKeyWithMissingParam.ts +++ b/local-tests/tests/wrapped-keys/testFailEthereumSignTransactionWrappedKeyWithMissingParam.ts @@ -66,11 +66,7 @@ export const testFailEthereumSignTransactionWrappedKeyWithMissingParam = async ( id, }); } catch (e: any) { - if ( - e.message.includes( - 'Error executing the Signing Lit Action: Error: Missing required field: toAddress' - ) - ) { + if (e.message.includes('Missing required field: toAddress')) { console.log('✅ THIS IS EXPECTED: ', e); console.log(e.message); console.log( From 35b33d5dc950612a666937225c1b3894924ba065 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 00:57:55 +0100 Subject: [PATCH 23/53] chore(lit-core): LIT-3920 - Revert debug log change --- packages/core/src/lib/lit-core.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/core/src/lib/lit-core.ts b/packages/core/src/lib/lit-core.ts index a7188a9305..3bc7a0f24f 100644 --- a/packages/core/src/lib/lit-core.ts +++ b/packages/core/src/lib/lit-core.ts @@ -36,6 +36,7 @@ import { loadModules, unloadModules, } from '@lit-protocol/crypto'; +import { LogLevel } from '@lit-protocol/logger'; import { bootstrapLogManager, isBrowser, @@ -71,7 +72,6 @@ import { } from '@lit-protocol/types'; import { composeLitUrl } from './endpoint-version'; -import { LogLevel } from '@lit-protocol/logger'; // eslint-disable-next-line @typescript-eslint/no-explicit-any type Listener = (...args: any[]) => void; @@ -1022,10 +1022,11 @@ export class LitCore { delete data.sessionSigs; } - logWithRequestId(requestId, `sendCommandToNode with url ${url} and data`, { - jsParams: JSON.stringify(data.jsParams), - codeLength: data.code?.length, - }); + logWithRequestId( + requestId, + `sendCommandToNode with url ${url} and data`, + data + ); const req: RequestInit = { method: 'POST', From 13f055691537c63c96d1143a3aded6b3d62a7bee Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 00:59:59 +0100 Subject: [PATCH 24/53] chore(wrapped-keys-lit-actions): LIT-3920 - Remove stack logging for prod --- .../src/lib/common/batchGenerateEncryptedKeys.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js b/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js index f94e3d7954..b004f4750e 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js +++ b/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js @@ -154,6 +154,6 @@ function validateParams(actions) { // 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 + err.stack}` }); + Lit.Actions.setResponse({ response: `Error: ${err.message}` }); } })(); From 8f9f029390696b53671e6005830dad4158d390d3 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 03:16:07 +0100 Subject: [PATCH 25/53] fix(wrapped-keys-lit-actions): LIT-3920 - Use runOnce on `processActions` to ensure signatures are consistent --- .../src/lib/common/batchGenerateEncryptedKeys.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js b/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js index b004f4750e..1b8c76b919 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js +++ b/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js @@ -144,10 +144,13 @@ function validateParams(actions) { try { validateParams(actions); - const batchGeneratePrivateKeysActionResult = await processActions(actions); + const batchGeneratePrivateKeysActionResult = await Lit.Actions.runOnce( + { waitForResponse: true, name: `processActions` }, + async () => JSON.stringify(await processActions(actions)) + ); Lit.Actions.setResponse({ - response: JSON.stringify(batchGeneratePrivateKeysActionResult), + response: batchGeneratePrivateKeysActionResult, }); // 1. Generate both EVM and solana private keys From 90a43cd6c3d3814b52b3b1cd194b5db9155f99e6 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 03:53:37 +0100 Subject: [PATCH 26/53] test(wrapped-keys): LIT-3920 - Add assertions for signatures to `batchGeneratePrivateKeys` test - It's worth noting that these pass (and the code works) even with the 'broken' implementation that didn't use runOnce - probably coincidentally the sdk returns the first node's result, and that node matches. --- .../testBatchGeneratePrivateKeys.ts | 85 ++++++++++++++++++- 1 file changed, 82 insertions(+), 3 deletions(-) diff --git a/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts b/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts index 2467fa0adc..080f69d6a6 100644 --- a/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts +++ b/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts @@ -2,8 +2,75 @@ import { log } from '@lit-protocol/misc'; import { TinnyEnvironment } from 'local-tests/setup/tinny-environment'; import { api } from '@lit-protocol/wrapped-keys'; import { getPkpSessionSigs } from 'local-tests/setup/session-sigs/get-pkp-session-sigs'; +import nacl from 'tweetnacl'; +import bs58 from 'bs58'; +import { ethers } from 'ethers'; +import { BatchGeneratePrivateKeysActionResult } from '../../../packages/wrapped-keys/src/lib/types'; -const { batchGeneratePrivateKeys } = api; +const { batchGeneratePrivateKeys, exportPrivateKey } = api; + +async function verifySolanaSignature( + solanaResult: BatchGeneratePrivateKeysActionResult, + solanaMessageToSign +) { + const { + signedMessage: { signature }, + generatedPrivateKey: { generatedPublicKey }, + } = solanaResult; + const signatureIsValidForPublicKey = nacl.sign.detached.verify( + Buffer.from(solanaMessageToSign), + bs58.decode(signature), + bs58.decode(generatedPublicKey) + ); + + console.log({ signatureIsValidForPublicKey, signature }); + if (!signatureIsValidForPublicKey) { + throw new Error( + `signature: ${signature} doesn't validate for the Solana public key: ${generatedPublicKey}` + ); + } +} +async function verifyEvmSignature( + litNodeClient, + evmResult, + messageToSign, + pkpSessionSigs +) { + function verifyMessageSignature() { + try { + return ethers.utils.verifyMessage( + messageToSign, + evmResult.signedMessage.signature + ); + } catch (err) { + throw new Error( + `When validating signed Ethereum message is valid: ${err.message}` + ); + } + } + + const { decryptedPrivateKey } = await exportPrivateKey({ + litNodeClient, + network: 'evm', + id: evmResult.generatedPrivateKey.id, + pkpSessionSigs, + }); + + const recoveredAddress = verifyMessageSignature(); + + const wallet = new ethers.Wallet(decryptedPrivateKey); + + console.log({ + recoveredAddress, + walletAddress: wallet.address, + signature: evmResult.signedMessage.signature, + }); + if (recoveredAddress !== wallet.address) { + throw new Error( + "Recovered address from verifyMessage doesn't match the wallet address" + ); + } +} /** * Test Commands: @@ -24,17 +91,19 @@ export const testBatchGeneratePrivateKeys = async ( new Date(Date.now() + 1000 * 60 * 10).toISOString() ); // 10 mins expiry + const solanaMessageToSign = 'This is a test solana message'; + const evmMessageToSign = 'This is a test evm message'; const results = await batchGeneratePrivateKeys({ pkpSessionSigs: pkpSessionSigsSigning, actions: [ { network: 'evm', - signMessageParams: { messageToSign: 'This is a test evm message' }, + signMessageParams: { messageToSign: evmMessageToSign }, generateKeyParams: { memo: 'Test evm key' }, }, { network: 'solana', - signMessageParams: { messageToSign: 'This is a test solana message' }, + signMessageParams: { messageToSign: solanaMessageToSign }, generateKeyParams: { memo: 'Test solana key' }, }, ], @@ -63,6 +132,16 @@ export const testBatchGeneratePrivateKeys = async ( throw new Error('Missing message signature in response'); } + console.log('solana verify sig'); + await verifySolanaSignature(results[1], solanaMessageToSign); + + console.log('evm verify sig'); + await verifyEvmSignature( + devEnv.litNodeClient, + results[0], + evmMessageToSign, + pkpSessionSigsSigning + ); console.log('results', results); log('✅ testBatchGenerateEncryptedKeys'); From f0ceff6a3364faa636aa70ce568598da68101418 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 04:04:22 +0100 Subject: [PATCH 27/53] chore(wrapped-keys): LIT-3920 - Object shorthand notation --- .../wrapped-keys/src/lib/api/batch-generate-private-keys.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wrapped-keys/src/lib/api/batch-generate-private-keys.ts b/packages/wrapped-keys/src/lib/api/batch-generate-private-keys.ts index af0dafaeeb..bf62fbe327 100644 --- a/packages/wrapped-keys/src/lib/api/batch-generate-private-keys.ts +++ b/packages/wrapped-keys/src/lib/api/batch-generate-private-keys.ts @@ -62,7 +62,7 @@ export async function batchGeneratePrivateKeys( ...(signature ? { signedMessage: { signature } } : {}), generatedPrivateKey: { memo: generatedPrivateKey.memo, - id: id, + id, generatedPublicKey: generatedPrivateKey.publicKey, pkpAddress, }, From e269157b67522763088c689ed2156a97e3860c94 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 05:09:17 +0100 Subject: [PATCH 28/53] chore(wrapped-keys-lit-actions): LIT-3920 - Add `sync-actions-to-ipfs` script that publishes current lit actions to IPFS and prints out common + by-network CID repository entries for `wrapped-keys` on completion --- .../sync-actions-to-ipfs.js | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 packages/wrapped-keys-lit-actions/sync-actions-to-ipfs.js diff --git a/packages/wrapped-keys-lit-actions/sync-actions-to-ipfs.js b/packages/wrapped-keys-lit-actions/sync-actions-to-ipfs.js new file mode 100644 index 0000000000..6d11a490ba --- /dev/null +++ b/packages/wrapped-keys-lit-actions/sync-actions-to-ipfs.js @@ -0,0 +1,94 @@ +const axios = require('axios'); +const FormData = require('form-data'); + +const { + litActionRepository, + litActionRepositoryCommon, +} = require('./dist/src/index'); + +const JWT = process.env.LIT_IPFS_JWT || ''; +if (!JWT) { + throw new Error('Missing Pinata IPFS JWT in LIT_IPFS_JWT env variable'); +} + +async function pinFileToIPFS(actionName, code) { + const formData = new FormData(); + formData.append('file', Buffer.from(code), { filename: actionName + '.js' }); + + formData.append( + 'pinataMetadata', + JSON.stringify({ + name: 'File name', + }) + ); + + formData.append( + 'pinataOptions', + JSON.stringify({ + cidVersion: 0, + }) + ); + + const res = await axios.post( + 'https://api.pinata.cloud/pinning/pinFileToIPFS', + formData, + { + maxBodyLength: 'Infinity', + headers: { + 'Content-Type': `multipart/form-data; boundary=${formData._boundary}`, + Authorization: `Bearer ${JWT}`, + }, + } + ); + + console.log(actionName, res.data.IpfsHash); + return res.data.IpfsHash; +} + +async function getCidRepository(codeRepository) { + const cidRepository = {}; + + await Promise.all( + Object.entries(codeRepository).map(([actionName, byNetworkObj]) => { + cidRepository[actionName] = {}; + + return Promise.all( + Object.entries(byNetworkObj).map(async ([networkName, codeStr]) => { + console.log('setting', actionName, networkName, codeStr.length); + + cidRepository[actionName][networkName] = await pinFileToIPFS( + actionName, + codeStr + ); + }) + ); + }) + ); + + return cidRepository; +} + +async function getCidRepositoryCommon(codeRepository) { + const cidRepository = {}; + + await Promise.all( + Object.entries(codeRepository).map(async ([actionName, codeStr]) => { + console.log('setting common', actionName, codeStr.length); + cidRepository[actionName] = await pinFileToIPFS(actionName, codeStr); + }) + ); + + return cidRepository; +} + +async function gogo() { + const [cidRepoCommon, cidRepo] = await Promise.all([ + getCidRepositoryCommon(litActionRepositoryCommon), + getCidRepository(litActionRepository), + ]); + + console.log('common', cidRepoCommon); + console.log('byNetwork', cidRepo); +} + +gogo(); From c3869d48b35668b2c0536e86ddab87fa8af438f4 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 15:34:29 +0100 Subject: [PATCH 29/53] chore(wrapped-keys-lit-actions): LIT-3920 - Move generation of private key into runOnce block --- .../ethereum/generateEncryptedEthereumPrivateKey.js | 10 +++++----- .../lib/solana/generateEncryptedSolanaPrivateKey.js | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js index 1abd82c20d..c6a82e3832 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js @@ -13,18 +13,18 @@ import { generateEthereumPrivateKey } from './internal/generatePrivateKey'; import { encryptPrivateKey } from '../common/internal/encryptKey'; (async () => { - const { privateKey, publicKey } = generateEthereumPrivateKey(); - const encryptedKeyResultStr = await Lit.Actions.runOnce( { waitForResponse: true, name: 'encryptEthereumPrivateKey' }, - async () => - JSON.stringify( + async () => { + const { privateKey, publicKey } = generateEthereumPrivateKey(); + return JSON.stringify( await encryptPrivateKey({ accessControlConditions, privateKey, publicKey, }) - ) + ); + } ); Lit.Actions.setResponse({ diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js index ae03089815..23f288c52d 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js @@ -12,18 +12,18 @@ const { encryptPrivateKey } = require('../common/internal/encryptKey'); * @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 encryptedKeyResultStr = await Lit.Actions.runOnce( { waitForResponse: true, name: 'encryptSolanaPrivateKey' }, - async () => - JSON.stringify( + async () => { + const { privateKey, publicKey } = generateSolanaPrivateKey(); + return JSON.stringify( await encryptPrivateKey({ accessControlConditions, publicKey, privateKey, }) - ) + ); + } ); Lit.Actions.setResponse({ From 82de82cfcd0c88195492805c27a998bf92447dd2 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 15:41:08 +0100 Subject: [PATCH 30/53] chore(wrapped-keys-lit-actions): LIT-3920 - Remove runOnce in `batchGenerateEncryptedKeys` --- .../lib/common/batchGenerateEncryptedKeys.js | 43 ++++++------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js b/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js index 1b8c76b919..ea0c86b322 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js +++ b/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js @@ -17,17 +17,11 @@ async function processEthereumAction(action, ndx) { const ethereumKey = generateEthereumPrivateKey(); const [generatedPrivateKey, messageSignature] = await Promise.all([ - Lit.Actions.runOnce( - { waitForResponse: true, name: `encryptEthereumPrivateKey_${ndx}` }, - async () => - JSON.stringify( - await encryptPrivateKey({ - accessControlConditions, - publicKey: ethereumKey.publicKey, - privateKey: ethereumKey.privateKey, - }) - ) - ), + encryptPrivateKey({ + accessControlConditions, + publicKey: ethereumKey.publicKey, + privateKey: ethereumKey.privateKey, + }), messageToSign ? signMessageEthereumKey({ messageToSign: messageToSign, @@ -39,7 +33,7 @@ async function processEthereumAction(action, ndx) { return { network, generatedPrivateKey: { - ...JSON.parse(generatedPrivateKey), + ...generatedPrivateKey, memo: generateKeyParams.memo, }, ...(messageSignature @@ -56,17 +50,11 @@ async function processSolanaAction(action, ndx) { const solanaKey = generateSolanaPrivateKey(); const [generatedPrivateKey, messageSignature] = await Promise.all([ - Lit.Actions.runOnce( - { waitForResponse: true, name: `encryptSolanaPrivateKey_${ndx}` }, - async () => - JSON.stringify( - await encryptPrivateKey({ - accessControlConditions, - publicKey: solanaKey.publicKey, - privateKey: solanaKey.privateKey, - }) - ) - ), + encryptPrivateKey({ + accessControlConditions, + publicKey: solanaKey.publicKey, + privateKey: solanaKey.privateKey, + }), messageToSign ? signMessageSolanaKey({ messageToSign: messageToSign, @@ -78,7 +66,7 @@ async function processSolanaAction(action, ndx) { return { network, generatedPrivateKey: { - ...JSON.parse(generatedPrivateKey), + ...generatedPrivateKey, memo: generateKeyParams.memo, }, ...(messageSignature @@ -144,13 +132,10 @@ function validateParams(actions) { try { validateParams(actions); - const batchGeneratePrivateKeysActionResult = await Lit.Actions.runOnce( - { waitForResponse: true, name: `processActions` }, - async () => JSON.stringify(await processActions(actions)) - ); + const batchGeneratePrivateKeysActionResult = await processActions(actions); Lit.Actions.setResponse({ - response: batchGeneratePrivateKeysActionResult, + response: JSON.stringify(batchGeneratePrivateKeysActionResult), }); // 1. Generate both EVM and solana private keys From 2d47564fc7d600988d787971ce30edca059ec877 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 16:59:11 +0100 Subject: [PATCH 31/53] chore(wrapped-keys-lit-actions): LIT-3920 - Make batchGeneratePrivateKeys return pkpAddress, for consistency with existing generatePrivateKeys implementation --- .../tests/wrapped-keys/testBatchGeneratePrivateKeys.ts | 1 + .../wrapped-keys/src/lib/api/batch-generate-private-keys.ts | 4 +++- packages/wrapped-keys/src/lib/types.ts | 6 ++++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts b/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts index 080f69d6a6..ece325ef85 100644 --- a/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts +++ b/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts @@ -94,6 +94,7 @@ export const testBatchGeneratePrivateKeys = async ( const solanaMessageToSign = 'This is a test solana message'; const evmMessageToSign = 'This is a test evm message'; const results = await batchGeneratePrivateKeys({ + const { results } = await batchGeneratePrivateKeys({ pkpSessionSigs: pkpSessionSigsSigning, actions: [ { diff --git a/packages/wrapped-keys/src/lib/api/batch-generate-private-keys.ts b/packages/wrapped-keys/src/lib/api/batch-generate-private-keys.ts index bf62fbe327..7833bd45c7 100644 --- a/packages/wrapped-keys/src/lib/api/batch-generate-private-keys.ts +++ b/packages/wrapped-keys/src/lib/api/batch-generate-private-keys.ts @@ -41,7 +41,7 @@ export async function batchGeneratePrivateKeys( pkpSessionSigs, }); - return Promise.all( + const results = await Promise.all( actionResults.map( async (result): Promise => { const { generatedPrivateKey, network } = result; @@ -70,4 +70,6 @@ export async function batchGeneratePrivateKeys( } ) ); + + return { pkpAddress, results }; } diff --git a/packages/wrapped-keys/src/lib/types.ts b/packages/wrapped-keys/src/lib/types.ts index 69b871160d..b86a5b047f 100644 --- a/packages/wrapped-keys/src/lib/types.ts +++ b/packages/wrapped-keys/src/lib/types.ts @@ -157,8 +157,10 @@ export interface BatchGeneratePrivateKeysActionResult { signedMessage?: { signature: string }; } -export type BatchGeneratePrivateKeysResult = - BatchGeneratePrivateKeysActionResult[]; +export interface BatchGeneratePrivateKeysResult { + pkpAddress: string; + results: BatchGeneratePrivateKeysActionResult[]; +} /** @typedef GeneratePrivateKeyParams * @extends BaseApiParams From f5bdd3fc57235f37e1d98c3c8f2403c095f71243 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 18:40:25 +0100 Subject: [PATCH 32/53] chore(wrapped-keys-lit-actions): LIT-3920 - Fix test double-line --- local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts b/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts index ece325ef85..335698e7da 100644 --- a/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts +++ b/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts @@ -93,7 +93,6 @@ export const testBatchGeneratePrivateKeys = async ( const solanaMessageToSign = 'This is a test solana message'; const evmMessageToSign = 'This is a test evm message'; - const results = await batchGeneratePrivateKeys({ const { results } = await batchGeneratePrivateKeys({ pkpSessionSigs: pkpSessionSigsSigning, actions: [ From 4874adc679b074ecbee03d812f595c300e5c7161 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 19:38:11 +0100 Subject: [PATCH 33/53] chore(wrapped-keys-lit-actions): LIT-3920 - Rename `getDecryptedKey` to `getDecryptedKeyToSingleNode` for clarity to readers --- .../src/lib/common/exportPrivateKey.js | 6 ++++-- .../{getDecryptedKey.js => getDecryptedKeyToSingleNode.js} | 2 +- .../src/lib/ethereum/signMessageWithEncryptedEthereumKey.js | 6 ++++-- .../lib/ethereum/signTransactionWithEncryptedEthereumKey.js | 6 ++++-- .../src/lib/solana/signMessageWithEncryptedSolanaKey.js | 6 ++++-- .../src/lib/solana/signTransactionWithEncryptedSolanaKey.js | 6 ++++-- 6 files changed, 21 insertions(+), 11 deletions(-) rename packages/wrapped-keys-lit-actions/src/lib/common/internal/{getDecryptedKey.js => getDecryptedKeyToSingleNode.js} (89%) diff --git a/packages/wrapped-keys-lit-actions/src/lib/common/exportPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/common/exportPrivateKey.js index 76271db03b..c99cb7280b 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/common/exportPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/common/exportPrivateKey.js @@ -1,4 +1,6 @@ -const { getDecryptedKey } = require('./internal/getDecryptedKey'); +const { + getDecryptedKeyToSingleNode, +} = require('./internal/getDecryptedKeyToSingleNode'); const { removeSaltFromDecryptedKey } = require('../utils'); /* global accessControlConditions, ciphertext, dataToEncryptHash, Lit */ @@ -17,7 +19,7 @@ const { removeSaltFromDecryptedKey } = require('../utils'); (async () => { try { - const decryptedPrivateKey = await getDecryptedKey({ + const decryptedPrivateKey = await getDecryptedKeyToSingleNode({ accessControlConditions, ciphertext, dataToEncryptHash, diff --git a/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKey.js b/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKeyToSingleNode.js similarity index 89% rename from packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKey.js rename to packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKeyToSingleNode.js index ba5d809a29..9f94fb0d02 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/common/internal/getDecryptedKeyToSingleNode.js @@ -1,6 +1,6 @@ /* global Lit */ -export async function getDecryptedKey({ +export async function getDecryptedKeyToSingleNode({ accessControlConditions, ciphertext, dataToEncryptHash, diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js index fe89b5d6ec..6ae679ea2d 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signMessageWithEncryptedEthereumKey.js @@ -1,5 +1,7 @@ const { signMessageEthereumKey } = require('./internal/signMessage'); -const { getDecryptedKey } = require('../common/internal/getDecryptedKey'); +const { + getDecryptedKeyToSingleNode, +} = require('../common/internal/getDecryptedKeyToSingleNode'); const { removeSaltFromDecryptedKey } = require('../utils'); /* global accessControlConditions, ciphertext, dataToEncryptHash, messageToSign, Lit */ @@ -18,7 +20,7 @@ const { removeSaltFromDecryptedKey } = require('../utils'); (async () => { try { - const decryptedPrivateKey = await getDecryptedKey({ + const decryptedPrivateKey = await getDecryptedKeyToSingleNode({ accessControlConditions, ciphertext, dataToEncryptHash, diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js index 8c7028cfe5..cddaf08386 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js @@ -1,5 +1,7 @@ const { signTransactionEthereumKey } = require('./internal/signTransaction'); -const { getDecryptedKey } = require('../common/internal/getDecryptedKey'); +const { + getDecryptedKeyToSingleNode, +} = require('../common/internal/getDecryptedKeyToSingleNode'); const { removeSaltFromDecryptedKey } = require('../utils'); /* global accessControlConditions, ciphertext, dataToEncryptHash, unsignedTransaction, broadcast, Lit */ @@ -19,7 +21,7 @@ const { removeSaltFromDecryptedKey } = require('../utils'); */ (async () => { try { - const decryptedPrivateKey = await getDecryptedKey({ + const decryptedPrivateKey = await getDecryptedKeyToSingleNode({ accessControlConditions, ciphertext, dataToEncryptHash, diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js index 6105cdac19..776b1fafcb 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/signMessageWithEncryptedSolanaKey.js @@ -1,5 +1,7 @@ const { signMessageSolanaKey } = require('./internal/signMessage'); -const { getDecryptedKey } = require('../common/internal/getDecryptedKey'); +const { + getDecryptedKeyToSingleNode, +} = require('../common/internal/getDecryptedKeyToSingleNode'); const { removeSaltFromDecryptedKey } = require('../utils'); /* global accessControlConditions, ciphertext, dataToEncryptHash, messageToSign, Lit */ @@ -19,7 +21,7 @@ const { removeSaltFromDecryptedKey } = require('../utils'); (async () => { try { - const decryptedPrivateKey = await getDecryptedKey({ + const decryptedPrivateKey = await getDecryptedKeyToSingleNode({ accessControlConditions, ciphertext, dataToEncryptHash, diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js index 5dc4b7d592..72d3820ad1 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js @@ -1,5 +1,7 @@ const { signTransactionSolanaKey } = require('./internal/signTransaction'); -const { getDecryptedKey } = require('../common/internal/getDecryptedKey'); +const { + getDecryptedKeyToSingleNode, +} = require('../common/internal/getDecryptedKeyToSingleNode'); const { removeSaltFromDecryptedKey } = require('../utils'); /* global accessControlConditions, ciphertext, dataToEncryptHash, unsignedTransaction, Lit, broadcast */ @@ -20,7 +22,7 @@ const { removeSaltFromDecryptedKey } = require('../utils'); (async () => { try { - const decryptedPrivateKey = await getDecryptedKey({ + const decryptedPrivateKey = await getDecryptedKeyToSingleNode({ accessControlConditions, ciphertext, dataToEncryptHash, From ea9c0876a24a22a292c5ffd453152a67bb1bc1a5 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 19:42:43 +0100 Subject: [PATCH 34/53] chore(wrapped-keys-lit-actions): LIT-3920 - Use existing ethers instance for base58 encoding instead of importing bs58 --- .../src/lib/solana/internal/signMessage.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessage.js b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessage.js index a44ec7b86a..aee2d285cf 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessage.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signMessage.js @@ -1,7 +1,8 @@ import { Keypair } from '@solana/web3.js'; -import bs58 from 'bs58'; import nacl from 'tweetnacl'; +/* global ethers */ + function signMessage({ messageToSign, solanaKeyPair }) { try { const signature = nacl.sign.detached( @@ -49,5 +50,5 @@ export async function signMessageSolanaKey({ messageToSign, privateKey }) { throw new Error('Signature did not verify to expected Solana public key'); } - return bs58.encode(signature); + return ethers.utils.base58.encode(signature); } From 8d517d8f248c173d817a0fe9ac097d6462d891fa Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 19:46:37 +0100 Subject: [PATCH 35/53] chore(wrapped-keys-lit-actions): LIT-3920 - Update IPFS CIDs to use current lit action code from this commit --- .../src/lib/lit-actions-client/constants.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/constants.ts b/packages/wrapped-keys/src/lib/lit-actions-client/constants.ts index 328daa46b6..27e8454950 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/constants.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/constants.ts @@ -2,25 +2,25 @@ import { LitCidRepository, LitCidRepositoryCommon } from './types'; const LIT_ACTION_CID_REPOSITORY: LitCidRepository = Object.freeze({ signTransaction: Object.freeze({ - evm: 'QmRWGips9G3pHXNa3viGFpAyh1LwzrR35R4xMiG61NuHpS', - solana: 'QmPZR6FnTPMYpzKxNNHt4xRckDsAoQz76cxkLhJArSoe4w', + evm: 'QmSr9TLHUFcGMrQQrNXRsYxcgaBFtcCqWi4sgbevbhmrhv', + solana: 'QmS223QiJkPfncUhY2HGQmme7LRgg93NBy8tU1fgGFUUzu', }), signMessage: Object.freeze({ - evm: 'QmNy5bHvgaN2rqo4kMU71jtgSSxDES6HSDgadBV29pfcRu', - solana: 'Qma1nRZN5eriT1a7Uffbiek5jsksvWCCqHuE1x1nk9zaAq', + evm: 'QmXi9iqJvXrHoUGSo5WREonrruDhzQ7cFr7Cry3wX2hmue', + solana: 'QmcEJGVqRYtVukjm2prCPT7Fs66GpaqZwmZoxEHMHor6Jz', }), generateEncryptedKey: Object.freeze({ - evm: 'QmaoPMSqcze3NW3KSA75ecWSkcmWT1J7kVr8LyJPCKRvHd', - solana: 'QmdRBXYLYvcNHrChmsZ2jFDY8dA99CcSdqHo3p1ES3UThL', + evm: 'QmPzycC9bN4QfhVWy7ggL4u9Gr1vQz3Gx4MYh4vvpvqBzc', + solana: 'QmYrmzhh7ZoK8LoZ854DmfdRacmZgpVz9c3kW6u5htcdc5', }), exportPrivateKey: Object.freeze({ - evm: 'Qmb5ZAm1EZRL7dYTtyYxkPxx4kBmoCjjzcgdrJH9cKMXxR', - solana: 'Qmb5ZAm1EZRL7dYTtyYxkPxx4kBmoCjjzcgdrJH9cKMXxR', + solana: 'QmUJ74pTUqeeHzDGdfwCph1vJVNJ1rRzJdvMiTjS1BMwYj', + evm: 'QmUJ74pTUqeeHzDGdfwCph1vJVNJ1rRzJdvMiTjS1BMwYj', }), }); const LIT_ACTION_CID_REPOSITORY_COMMON: LitCidRepositoryCommon = Object.freeze({ - batchGenerateEncryptedKeys: '', + batchGenerateEncryptedKeys: 'QmTMCnEd5M45EVSBvayc8gAvFsNYRnPjf5sfbtR44bWt68', }); export { LIT_ACTION_CID_REPOSITORY, LIT_ACTION_CID_REPOSITORY_COMMON }; From 15337ad638e0671d5c94ecc20ef844b2db0a9767 Mon Sep 17 00:00:00 2001 From: Anson Date: Wed, 9 Oct 2024 18:18:48 +0100 Subject: [PATCH 36/53] feat: add advanced controls to executeJs --- .../src/lib/lit-node-client-nodejs.ts | 23 +++--- packages/types/src/lib/interfaces.ts | 81 ++++++++++++------- 2 files changed, 61 insertions(+), 43 deletions(-) diff --git a/packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts b/packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts index db31ff00f7..d4946754cc 100644 --- a/packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts +++ b/packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts @@ -135,8 +135,7 @@ import type { export class LitNodeClientNodeJs extends LitCore - implements LitClientSessionManager, ILitNodeClient -{ + implements LitClientSessionManager, ILitNodeClient { defaultAuthCallback?: (authSigParams: AuthCallbackParams) => Promise; // ========== Constructor ========== @@ -1098,7 +1097,7 @@ export class LitNodeClientNodeJs const res = await this.handleNodePromises( nodePromises, requestId, - this.connectedNodes.size + params.numResponsesRequired || this.connectedNodes.size ); // -- case: promises rejected @@ -1162,7 +1161,7 @@ export class LitNodeClientNodeJs const signatures = getSignatures({ requestId, networkPubKeySet: this.networkPubKeySet, - minNodeCount: this.config.minNodeCount, + minNodeCount: params.numResponsesRequired || this.config.minNodeCount, signedData: signedDataList, }); @@ -1277,8 +1276,8 @@ export class LitNodeClientNodeJs // -- optional params ...(params.authMethods && params.authMethods.length > 0 && { - authMethods: params.authMethods, - }), + authMethods: params.authMethods, + }), }; logWithRequestId(requestId, 'reqBody:', reqBody); @@ -2080,8 +2079,8 @@ export class LitNodeClientNodeJs const sessionCapabilityObject = params.sessionCapabilityObject ? params.sessionCapabilityObject : await this.generateSessionCapabilityObjectWithWildcards( - params.resourceAbilityRequests.map((r) => r.resource) - ); + params.resourceAbilityRequests.map((r) => r.resource) + ); const expiration = params.expiration || LitNodeClientNodeJs.getExpiration(); // -- (TRY) to get the wallet signature @@ -2164,10 +2163,10 @@ export class LitNodeClientNodeJs const capabilities = params.capacityDelegationAuthSig ? [ - ...(params.capabilityAuthSigs ?? []), - params.capacityDelegationAuthSig, - authSig, - ] + ...(params.capabilityAuthSigs ?? []), + params.capacityDelegationAuthSig, + authSig, + ] : [...(params.capabilityAuthSigs ?? []), authSig]; const signingTemplate = { diff --git a/packages/types/src/lib/interfaces.ts b/packages/types/src/lib/interfaces.ts index d2a5f865ff..bda7aea7c4 100644 --- a/packages/types/src/lib/interfaces.ts +++ b/packages/types/src/lib/interfaces.ts @@ -314,7 +314,7 @@ export interface JsonSignChainDataRequest { export interface JsonSignSessionKeyRequestV1 extends Pick, - Pick { + Pick { sessionKey: string; authMethods: AuthMethod[]; pkpPublicKey?: string; @@ -502,7 +502,8 @@ export interface JsonExecutionSdkParamsTargetNode } export interface JsonExecutionSdkParams - extends Pick { + extends Pick, + ExecuteJsAdvancedOptions { /** * JS code to run on the nodes */ @@ -522,6 +523,9 @@ export interface JsonExecutionSdkParams * auth methods to resolve */ authMethods?: AuthMethod[]; +} + +export interface ExecuteJsAdvancedOptions { /** * a strategy for proccessing `reponse` objects returned from the @@ -529,7 +533,20 @@ export interface JsonExecutionSdkParams */ responseStrategy?: LitActionResponseStrategy; + /** + * Allow overriding the default `code` property in the `JsonExecutionSdkParams` + */ ipfsOptions?: IpfsOptions; + + /** + * number of responses required to consider the execution successful + */ + numResponsesRequired?: number; + + /** + * idea: the number of nodes to pay for running executions + */ + // numNodesToRunOn?: number; } export interface JsonExecutionRequestTargetNode extends JsonExecutionRequest { @@ -568,7 +585,7 @@ export interface SessionSigsOrAuthSig { export interface DecryptRequestBase extends SessionSigsOrAuthSig, - MultipleAccessControlConditions { + MultipleAccessControlConditions { /** * The chain name of the chain that this contract is deployed on. See LIT_CHAINS for currently supported chains. */ @@ -613,7 +630,7 @@ export interface EncryptFileRequest extends DecryptRequestBase { file: AcceptedFileType; } -export interface DecryptRequest extends EncryptResponse, DecryptRequestBase {} +export interface DecryptRequest extends EncryptResponse, DecryptRequestBase { } export interface DecryptResponse { // The decrypted data as a Uint8Array @@ -635,10 +652,10 @@ export interface SigResponse { export interface ExecuteJsResponseBase { signatures: - | { - sig: SigResponse; - } - | any; + | { + sig: SigResponse; + } + | any; } /** @@ -668,7 +685,7 @@ export interface ExecuteJsNoSigningResponse extends ExecuteJsResponseBase { logs: string; } -export interface LitNodePromise {} +export interface LitNodePromise { } export interface SendNodeCommand { url: string; @@ -677,17 +694,17 @@ export interface SendNodeCommand { } export interface SigShare { sigType: - | 'BLS' - | 'K256' - | 'ECDSA_CAIT_SITH' // Legacy alias of K256 - | 'EcdsaCaitSithP256'; + | 'BLS' + | 'K256' + | 'ECDSA_CAIT_SITH' // Legacy alias of K256 + | 'EcdsaCaitSithP256'; signatureShare: string; shareIndex?: number; bigr?: string; // backward compatibility bigR?: string; publicKey: string; - dataSigned?: string; + dataSigned?: string | 'fail'; siweMessage?: string; sigName?: string; } @@ -704,6 +721,8 @@ export interface PkpSignedData { export interface NodeShare { claimData: any; shareIndex: any; + + // I think this is deprecated unsignedJwt: any; signedData: SigShare; decryptedData: any; @@ -1146,7 +1165,7 @@ export interface CommonGetSessionSigsProps { export interface BaseProviderGetSessionSigsProps extends CommonGetSessionSigsProps, - LitActionSdkParams { + LitActionSdkParams { /** * This is a callback that will be used to generate an AuthSig within the session signatures. It's inclusion is required, as it defines the specific resources and abilities that will be allowed for the current session. */ @@ -1155,7 +1174,7 @@ export interface BaseProviderGetSessionSigsProps export interface GetSessionSigsProps extends CommonGetSessionSigsProps, - LitActionSdkParams { + LitActionSdkParams { /** * This is a callback that will be used to generate an AuthSig within the session signatures. It's inclusion is required, as it defines the specific resources and abilities that will be allowed for the current session. */ @@ -1679,7 +1698,7 @@ export interface LoginUrlParams { error: string | null; } -export interface BaseAuthenticateOptions {} +export interface BaseAuthenticateOptions { } export interface EthWalletAuthenticateOptions extends BaseAuthenticateOptions { /** @@ -1745,9 +1764,9 @@ export interface MintCapacityCreditsPerKilosecond } export interface MintCapacityCreditsContext extends MintCapacityCreditsPerDay, - MintCapacityCreditsPerSecond, - MintCapacityCreditsPerKilosecond, - GasLimitParam {} + MintCapacityCreditsPerSecond, + MintCapacityCreditsPerKilosecond, + GasLimitParam { } export interface MintCapacityCreditsRes { rliTxHash: string; capacityTokenId: any; @@ -1870,12 +1889,12 @@ export interface LitActionSdkParams { * An object that contains params to expose to the Lit Action. These will be injected to the JS runtime before your code runs, so you can use any of these as normal variables in your Lit Action. */ jsParams?: - | { - [key: string]: any; - publicKey?: string; - sigName?: string; - } - | any; + | { + [key: string]: any; + publicKey?: string; + sigName?: string; + } + | any; } export interface LitEndpoint { @@ -1897,7 +1916,7 @@ export interface SignerLike { export interface GetPkpSessionSigs extends CommonGetSessionSigsProps, - LitActionSdkParams { + LitActionSdkParams { pkpPublicKey: string; /** @@ -1923,11 +1942,11 @@ export type GetLitActionSessionSigs = CommonGetSessionSigsProps & Pick, 'jsParams'> & ( | (Pick, 'litActionCode'> & { - litActionIpfsId?: never; - }) + litActionIpfsId?: never; + }) | (Pick, 'litActionIpfsId'> & { - litActionCode?: never; - }) + litActionCode?: never; + }) ) & { ipfsOptions?: IpfsOptions; }; From 7db654f69169822324ed432919222184c0f0cad7 Mon Sep 17 00:00:00 2001 From: Anson Date: Wed, 9 Oct 2024 18:19:00 +0100 Subject: [PATCH 37/53] chore: better error message --- .../lit-node-client-nodejs/src/lib/helpers/get-signatures.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lit-node-client-nodejs/src/lib/helpers/get-signatures.ts b/packages/lit-node-client-nodejs/src/lib/helpers/get-signatures.ts index 4d25dc265f..0ecab7084e 100644 --- a/packages/lit-node-client-nodejs/src/lib/helpers/get-signatures.ts +++ b/packages/lit-node-client-nodejs/src/lib/helpers/get-signatures.ts @@ -146,7 +146,7 @@ export const getSignatures = (params: { if (allKeys.length !== initialKeys.length) { throwError({ - message: 'total number of valid signatures does not match requested', + message: `Total number of valid signatures does not match requested. Valid signatures: ${allKeys.length}, Requested signatures: ${initialKeys.length}`, errorKind: LIT_ERROR.NO_VALID_SHARES.kind, errorCode: LIT_ERROR.NO_VALID_SHARES.code, }); From bae0338f81c1b191f9c70116e68aee937e7364b8 Mon Sep 17 00:00:00 2001 From: Anson Date: Wed, 9 Oct 2024 18:19:22 +0100 Subject: [PATCH 38/53] feat(tinny): add test --- local-tests/test.ts | 2 + ...seEoaSessionSigsToRequestSingleResponse.ts | 55 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts diff --git a/local-tests/test.ts b/local-tests/test.ts index 751782294a..431460344b 100644 --- a/local-tests/test.ts +++ b/local-tests/test.ts @@ -107,6 +107,7 @@ import { testSignTransactionWithSolanaEncryptedKey } from './tests/wrapped-keys/ import { testBatchGeneratePrivateKeys } from './tests/wrapped-keys/testBatchGeneratePrivateKeys'; import { setLitActionsCodeToLocal } from './tests/wrapped-keys/util'; +import { testUseEoaSessionSigsToRequestSingleResponse } from './tests/testUseEoaSessionSigsToRequestSingleResponse'; // Use the current LIT action code to test against setLitActionsCodeToLocal(); @@ -170,6 +171,7 @@ setLitActionsCodeToLocal(); testUseEoaSessionSigsToEncryptDecryptString, testUseEoaSessionSigsToEncryptDecryptFile, testUseEoaSessionSigsToEncryptDecryptZip, + testUseEoaSessionSigsToRequestSingleResponse, }; const pkpSessionSigsTests = { diff --git a/local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts b/local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts new file mode 100644 index 0000000000..6b780463c3 --- /dev/null +++ b/local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts @@ -0,0 +1,55 @@ +import { getEoaSessionSigs } from 'local-tests/setup/session-sigs/get-eoa-session-sigs'; +import { TinnyEnvironment } from 'local-tests/setup/tinny-environment'; + +/** + * Test Commands: + * ✅ NETWORK=datil-dev yarn test:local --filter=testUseEoaSessionSigsToRequestSingleResponse + * ✅ NETWORK=datil-test yarn test:local --filter=testUseEoaSessionSigsToRequestSingleResponse + * ✅ NETWORK=datil yarn test:local --filter=testUseEoaSessionSigsToRequestSingleResponse + */ +export const testUseEoaSessionSigsToRequestSingleResponse = async ( + devEnv: TinnyEnvironment +) => { + const alice = await devEnv.createRandomPerson(); + + const eoaSessionSigs = await getEoaSessionSigs(devEnv, alice); + + const res = await devEnv.litNodeClient.executeJs({ + sessionSigs: eoaSessionSigs, + code: `(async () => { + console.log('hello world') + })();`, + numResponsesRequired: 1, + }); + + devEnv.releasePrivateKeyFromUser(alice); + + console.log('res:', res); + + // Expected output: + // { + // success: true, + // signedData: {}, + // decryptedData: {}, + // claimData: {}, + // response: "", + // logs: "hello world\n", + // } + + // -- assertions + if (res.response) { + throw new Error(`Expected "response" to be falsy`); + } + + if (!res.logs) { + throw new Error(`Expected "logs" in res`); + } + + if (!res.logs.includes('hello world')) { + throw new Error(`Expected "logs" to include 'hello world'`); + } + + if (!res.success) { + throw new Error(`Expected "success" in res`); + } +}; From d6644725ecae346b55c057aeb4d945227d2109b0 Mon Sep 17 00:00:00 2001 From: Anson Date: Wed, 9 Oct 2024 18:19:56 +0100 Subject: [PATCH 39/53] fmt --- .../src/lib/lit-node-client-nodejs.ts | 19 +++--- packages/types/src/lib/interfaces.ts | 61 +++++++++---------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts b/packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts index d4946754cc..c3dff30504 100644 --- a/packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts +++ b/packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts @@ -135,7 +135,8 @@ import type { export class LitNodeClientNodeJs extends LitCore - implements LitClientSessionManager, ILitNodeClient { + implements LitClientSessionManager, ILitNodeClient +{ defaultAuthCallback?: (authSigParams: AuthCallbackParams) => Promise; // ========== Constructor ========== @@ -1276,8 +1277,8 @@ export class LitNodeClientNodeJs // -- optional params ...(params.authMethods && params.authMethods.length > 0 && { - authMethods: params.authMethods, - }), + authMethods: params.authMethods, + }), }; logWithRequestId(requestId, 'reqBody:', reqBody); @@ -2079,8 +2080,8 @@ export class LitNodeClientNodeJs const sessionCapabilityObject = params.sessionCapabilityObject ? params.sessionCapabilityObject : await this.generateSessionCapabilityObjectWithWildcards( - params.resourceAbilityRequests.map((r) => r.resource) - ); + params.resourceAbilityRequests.map((r) => r.resource) + ); const expiration = params.expiration || LitNodeClientNodeJs.getExpiration(); // -- (TRY) to get the wallet signature @@ -2163,10 +2164,10 @@ export class LitNodeClientNodeJs const capabilities = params.capacityDelegationAuthSig ? [ - ...(params.capabilityAuthSigs ?? []), - params.capacityDelegationAuthSig, - authSig, - ] + ...(params.capabilityAuthSigs ?? []), + params.capacityDelegationAuthSig, + authSig, + ] : [...(params.capabilityAuthSigs ?? []), authSig]; const signingTemplate = { diff --git a/packages/types/src/lib/interfaces.ts b/packages/types/src/lib/interfaces.ts index bda7aea7c4..b626bd708f 100644 --- a/packages/types/src/lib/interfaces.ts +++ b/packages/types/src/lib/interfaces.ts @@ -314,7 +314,7 @@ export interface JsonSignChainDataRequest { export interface JsonSignSessionKeyRequestV1 extends Pick, - Pick { + Pick { sessionKey: string; authMethods: AuthMethod[]; pkpPublicKey?: string; @@ -503,7 +503,7 @@ export interface JsonExecutionSdkParamsTargetNode export interface JsonExecutionSdkParams extends Pick, - ExecuteJsAdvancedOptions { + ExecuteJsAdvancedOptions { /** * JS code to run on the nodes */ @@ -526,7 +526,6 @@ export interface JsonExecutionSdkParams } export interface ExecuteJsAdvancedOptions { - /** * a strategy for proccessing `reponse` objects returned from the * Lit Action execution context @@ -585,7 +584,7 @@ export interface SessionSigsOrAuthSig { export interface DecryptRequestBase extends SessionSigsOrAuthSig, - MultipleAccessControlConditions { + MultipleAccessControlConditions { /** * The chain name of the chain that this contract is deployed on. See LIT_CHAINS for currently supported chains. */ @@ -630,7 +629,7 @@ export interface EncryptFileRequest extends DecryptRequestBase { file: AcceptedFileType; } -export interface DecryptRequest extends EncryptResponse, DecryptRequestBase { } +export interface DecryptRequest extends EncryptResponse, DecryptRequestBase {} export interface DecryptResponse { // The decrypted data as a Uint8Array @@ -652,10 +651,10 @@ export interface SigResponse { export interface ExecuteJsResponseBase { signatures: - | { - sig: SigResponse; - } - | any; + | { + sig: SigResponse; + } + | any; } /** @@ -685,7 +684,7 @@ export interface ExecuteJsNoSigningResponse extends ExecuteJsResponseBase { logs: string; } -export interface LitNodePromise { } +export interface LitNodePromise {} export interface SendNodeCommand { url: string; @@ -694,10 +693,10 @@ export interface SendNodeCommand { } export interface SigShare { sigType: - | 'BLS' - | 'K256' - | 'ECDSA_CAIT_SITH' // Legacy alias of K256 - | 'EcdsaCaitSithP256'; + | 'BLS' + | 'K256' + | 'ECDSA_CAIT_SITH' // Legacy alias of K256 + | 'EcdsaCaitSithP256'; signatureShare: string; shareIndex?: number; @@ -1165,7 +1164,7 @@ export interface CommonGetSessionSigsProps { export interface BaseProviderGetSessionSigsProps extends CommonGetSessionSigsProps, - LitActionSdkParams { + LitActionSdkParams { /** * This is a callback that will be used to generate an AuthSig within the session signatures. It's inclusion is required, as it defines the specific resources and abilities that will be allowed for the current session. */ @@ -1174,7 +1173,7 @@ export interface BaseProviderGetSessionSigsProps export interface GetSessionSigsProps extends CommonGetSessionSigsProps, - LitActionSdkParams { + LitActionSdkParams { /** * This is a callback that will be used to generate an AuthSig within the session signatures. It's inclusion is required, as it defines the specific resources and abilities that will be allowed for the current session. */ @@ -1698,7 +1697,7 @@ export interface LoginUrlParams { error: string | null; } -export interface BaseAuthenticateOptions { } +export interface BaseAuthenticateOptions {} export interface EthWalletAuthenticateOptions extends BaseAuthenticateOptions { /** @@ -1764,9 +1763,9 @@ export interface MintCapacityCreditsPerKilosecond } export interface MintCapacityCreditsContext extends MintCapacityCreditsPerDay, - MintCapacityCreditsPerSecond, - MintCapacityCreditsPerKilosecond, - GasLimitParam { } + MintCapacityCreditsPerSecond, + MintCapacityCreditsPerKilosecond, + GasLimitParam {} export interface MintCapacityCreditsRes { rliTxHash: string; capacityTokenId: any; @@ -1889,12 +1888,12 @@ export interface LitActionSdkParams { * An object that contains params to expose to the Lit Action. These will be injected to the JS runtime before your code runs, so you can use any of these as normal variables in your Lit Action. */ jsParams?: - | { - [key: string]: any; - publicKey?: string; - sigName?: string; - } - | any; + | { + [key: string]: any; + publicKey?: string; + sigName?: string; + } + | any; } export interface LitEndpoint { @@ -1916,7 +1915,7 @@ export interface SignerLike { export interface GetPkpSessionSigs extends CommonGetSessionSigsProps, - LitActionSdkParams { + LitActionSdkParams { pkpPublicKey: string; /** @@ -1942,11 +1941,11 @@ export type GetLitActionSessionSigs = CommonGetSessionSigsProps & Pick, 'jsParams'> & ( | (Pick, 'litActionCode'> & { - litActionIpfsId?: never; - }) + litActionIpfsId?: never; + }) | (Pick, 'litActionIpfsId'> & { - litActionCode?: never; - }) + litActionCode?: never; + }) ) & { ipfsOptions?: IpfsOptions; }; From 77cefc2f7f53ce638f883f2b8fae0a4021afddb2 Mon Sep 17 00:00:00 2001 From: Anson Date: Wed, 9 Oct 2024 18:33:16 +0100 Subject: [PATCH 40/53] Update local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts Co-authored-by: Daryl Collins Signed-off-by: Anson --- ...seEoaSessionSigsToRequestSingleResponse.ts | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts b/local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts index 6b780463c3..d057aff8ef 100644 --- a/local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts +++ b/local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts @@ -12,18 +12,19 @@ export const testUseEoaSessionSigsToRequestSingleResponse = async ( ) => { const alice = await devEnv.createRandomPerson(); - const eoaSessionSigs = await getEoaSessionSigs(devEnv, alice); - - const res = await devEnv.litNodeClient.executeJs({ - sessionSigs: eoaSessionSigs, - code: `(async () => { - console.log('hello world') - })();`, - numResponsesRequired: 1, - }); - - devEnv.releasePrivateKeyFromUser(alice); - + try { + const eoaSessionSigs = await getEoaSessionSigs(devEnv, alice); + + const res = await devEnv.litNodeClient.executeJs({ + sessionSigs: eoaSessionSigs, + code: `(async () => { + console.log('hello world') + })();`, + numResponsesRequired: 1, + }); + } finally { + devEnv.releasePrivateKeyFromUser(alice); + } console.log('res:', res); // Expected output: From 9a3af57afd0cc72dd0b13c2d1dcedeeb7aeb9f5c Mon Sep 17 00:00:00 2001 From: Anson Date: Wed, 9 Oct 2024 18:37:13 +0100 Subject: [PATCH 41/53] fmt --- ...seEoaSessionSigsToRequestSingleResponse.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts b/local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts index d057aff8ef..c2b10f4ac7 100644 --- a/local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts +++ b/local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts @@ -13,18 +13,18 @@ export const testUseEoaSessionSigsToRequestSingleResponse = async ( const alice = await devEnv.createRandomPerson(); try { - const eoaSessionSigs = await getEoaSessionSigs(devEnv, alice); - - const res = await devEnv.litNodeClient.executeJs({ - sessionSigs: eoaSessionSigs, - code: `(async () => { + const eoaSessionSigs = await getEoaSessionSigs(devEnv, alice); + + const res = await devEnv.litNodeClient.executeJs({ + sessionSigs: eoaSessionSigs, + code: `(async () => { console.log('hello world') })();`, - numResponsesRequired: 1, - }); - } finally { - devEnv.releasePrivateKeyFromUser(alice); - } + numResponsesRequired: 1, + }); + } finally { + devEnv.releasePrivateKeyFromUser(alice); + } console.log('res:', res); // Expected output: From 7f7b38735b517320087393ff085f9885ee3bd00a Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 19:53:06 +0100 Subject: [PATCH 42/53] chore(wrapped-keys-lit-actions): LIT-3920 - Update `batchGenerateKeysWithLitAction()` to set `numResponsesRequired: 1` on its `executeJs()` call --- .../src/lib/lit-actions-client/batch-generate-keys.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/batch-generate-keys.ts b/packages/wrapped-keys/src/lib/lit-actions-client/batch-generate-keys.ts index 84cf2ff04d..c8f27e73ce 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/batch-generate-keys.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/batch-generate-keys.ts @@ -37,6 +37,7 @@ export async function batchGenerateKeysWithLitAction( } = args; const result = await litNodeClient.executeJs({ + numResponsesRequired: 1, sessionSigs: pkpSessionSigs, ipfsId: litActionIpfsCid, code: litActionCode, From 8e6b8cd8977ad371abe680552d0c098a1dd778e4 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 20:09:44 +0100 Subject: [PATCH 43/53] chore(wrapped-keys-lit-actions): LIT-3920 - Add package dependencies for `sync-actions-to-ipfs` script - Updated axios dependency to be a dev dependency (we don't use it in any of our SDK packages), using ^1.6.0 since that was already in our yarn.lock - Added `form-date` as a dev dependency --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index bd2621b297..a4d1e90913 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,6 @@ "@walletconnect/utils": "2.9.2", "@walletconnect/web3wallet": "1.8.8", "ajv": "^8.12.0", - "axios": "^0.27.2", "base64url": "^3.0.1", "bitcoinjs-lib": "^6.1.0", "bs58": "^5.0.0", @@ -100,6 +99,7 @@ "@types/secp256k1": "^4.0.6", "@typescript-eslint/eslint-plugin": "6.21.0", "@typescript-eslint/parser": "6.21.0", + "axios": "^1.6.0", "babel-jest": "27.5.1", "body-parser": "^1.20.2", "buffer": "^6.0.3", @@ -120,6 +120,7 @@ "eslint-plugin-react-hooks": "4.6.0", "ethereum-abi-types-generator": "^1.3.2", "express": "^4.18.2", + "form-data": "^4.0.0", "inquirer": "^9.2.21", "ipfs-unixfs-importer": "12.0.1", "jest": "27.5.1", From 54053fe86e513b726213be199c2eaadcf9890da7 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 22:38:51 +0100 Subject: [PATCH 44/53] chore(wrapped-keys-lit-actions): LIT-3920 - Add usage instructions to sync-actions-to-ipfs.js --- packages/wrapped-keys-lit-actions/sync-actions-to-ipfs.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/wrapped-keys-lit-actions/sync-actions-to-ipfs.js b/packages/wrapped-keys-lit-actions/sync-actions-to-ipfs.js index 6d11a490ba..5fbba5d2f0 100644 --- a/packages/wrapped-keys-lit-actions/sync-actions-to-ipfs.js +++ b/packages/wrapped-keys-lit-actions/sync-actions-to-ipfs.js @@ -6,6 +6,14 @@ const { litActionRepositoryCommon, } = require('./dist/src/index'); +/** Usage: + * 1. Ensure you have a valid Pinata IPFS JWT in `LIT_IPFS_JWT` env var + * 2. Make sure you run `yarn build` to ensure that all LIT actions code has been built into the generated directory from the current commit + * 3. `node sync-actions-to-ipfs` -> this will print out JSON of the `LIT_ACTION_CID_REPOSITORY` and LIT_ACTION_CID_REPOSITORY_COMMON + * 4. Copy/paste the CIDs into those objects in `packages/wrapped-keys/src/lib/lit-actions-client/constants.ts` + * 5. Commit the changes and push them to your branch + */ + const JWT = process.env.LIT_IPFS_JWT || ''; if (!JWT) { throw new Error('Missing Pinata IPFS JWT in LIT_IPFS_JWT env variable'); From 765b437a6e369ac895030b04139a9628c6a5dc7c Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 23:08:18 +0100 Subject: [PATCH 45/53] chore(wrapped-keys-lit-actions): LIT-3920 - Rename `generatedPrivateKey` -> `generateEncryptedPrivateKey` and `signedMessage` -> `signMessage` --- .../testBatchGeneratePrivateKeys.ts | 22 +++++++++---------- .../lib/common/batchGenerateEncryptedKeys.js | 12 +++++----- .../lib/api/batch-generate-private-keys.ts | 14 ++++++------ .../lit-actions-client/batch-generate-keys.ts | 4 ++-- packages/wrapped-keys/src/lib/types.ts | 4 ++-- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts b/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts index 335698e7da..8a2aefba76 100644 --- a/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts +++ b/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts @@ -14,8 +14,8 @@ async function verifySolanaSignature( solanaMessageToSign ) { const { - signedMessage: { signature }, - generatedPrivateKey: { generatedPublicKey }, + signMessage: { signature }, + generateEncryptedPrivateKey: { generatedPublicKey }, } = solanaResult; const signatureIsValidForPublicKey = nacl.sign.detached.verify( Buffer.from(solanaMessageToSign), @@ -40,7 +40,7 @@ async function verifyEvmSignature( try { return ethers.utils.verifyMessage( messageToSign, - evmResult.signedMessage.signature + evmResult.signMessage.signature ); } catch (err) { throw new Error( @@ -52,7 +52,7 @@ async function verifyEvmSignature( const { decryptedPrivateKey } = await exportPrivateKey({ litNodeClient, network: 'evm', - id: evmResult.generatedPrivateKey.id, + id: evmResult.generateEncryptedPrivateKey.id, pkpSessionSigs, }); @@ -63,7 +63,7 @@ async function verifyEvmSignature( console.log({ recoveredAddress, walletAddress: wallet.address, - signature: evmResult.signedMessage.signature, + signature: evmResult.signMessage.signature, }); if (recoveredAddress !== wallet.address) { throw new Error( @@ -74,8 +74,8 @@ async function verifyEvmSignature( /** * Test Commands: - * ✅ NETWORK=cayenne yarn test:local --filter=testSignMessageWithSolanaEncryptedKey - * ✅ NETWORK=manzano yarn test:local --filter=testSignMessageWithSolanaEncryptedKey + * ✅ NETWORK=datil-dev yarn test:local --filter=testSignMessageWithSolanaEncryptedKey + * ✅ NETWORK=datil-test yarn test:local --filter=testSignMessageWithSolanaEncryptedKey * ✅ NETWORK=localchain yarn test:local --filter=testSignMessageWithSolanaEncryptedKey */ export const testBatchGeneratePrivateKeys = async ( @@ -117,8 +117,8 @@ export const testBatchGeneratePrivateKeys = async ( } if ( - results[0].generatedPrivateKey.memo !== 'Test evm key' || - results[1].generatedPrivateKey.memo !== 'Test solana key' + results[0].generateEncryptedPrivateKey.memo !== 'Test evm key' || + results[1].generateEncryptedPrivateKey.memo !== 'Test solana key' ) { throw new Error( 'Results not in order sent; expected evm as first result, solana as second' @@ -126,8 +126,8 @@ export const testBatchGeneratePrivateKeys = async ( } if ( - !results[0].signedMessage.signature || - !results[1].signedMessage.signature + !results[0].signMessage.signature || + !results[1].signMessage.signature ) { throw new Error('Missing message signature in response'); } diff --git a/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js b/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js index ea0c86b322..cb8df59981 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js +++ b/packages/wrapped-keys-lit-actions/src/lib/common/batchGenerateEncryptedKeys.js @@ -10,7 +10,7 @@ const { signMessageSolanaKey } = require('../solana/internal/signMessage'); /* global accessControlConditions, actions, Lit*/ -async function processEthereumAction(action, ndx) { +async function processEthereumAction(action) { const { network, generateKeyParams } = action; const messageToSign = action.signMessageParams?.messageToSign; @@ -32,17 +32,17 @@ async function processEthereumAction(action, ndx) { return { network, - generatedPrivateKey: { + generateEncryptedPrivateKey: { ...generatedPrivateKey, memo: generateKeyParams.memo, }, ...(messageSignature - ? { signedMessage: { signature: messageSignature } } + ? { signMessage: { signature: messageSignature } } : {}), }; } -async function processSolanaAction(action, ndx) { +async function processSolanaAction(action) { const { network, generateKeyParams } = action; const messageToSign = action.signMessageParams?.messageToSign; @@ -65,12 +65,12 @@ async function processSolanaAction(action, ndx) { return { network, - generatedPrivateKey: { + generateEncryptedPrivateKey: { ...generatedPrivateKey, memo: generateKeyParams.memo, }, ...(messageSignature - ? { signedMessage: { signature: messageSignature } } + ? { signMessage: { signature: messageSignature } } : {}), }; } diff --git a/packages/wrapped-keys/src/lib/api/batch-generate-private-keys.ts b/packages/wrapped-keys/src/lib/api/batch-generate-private-keys.ts index 7833bd45c7..5c2313bb50 100644 --- a/packages/wrapped-keys/src/lib/api/batch-generate-private-keys.ts +++ b/packages/wrapped-keys/src/lib/api/batch-generate-private-keys.ts @@ -44,14 +44,14 @@ export async function batchGeneratePrivateKeys( const results = await Promise.all( actionResults.map( async (result): Promise => { - const { generatedPrivateKey, network } = result; + const { generateEncryptedPrivateKey, network } = result; - const signature = result.signedMessage?.signature; + const signature = result.signMessage?.signature; const { id } = await storePrivateKey({ sessionSig, storedKeyMetadata: { - ...generatedPrivateKey, + ...generateEncryptedPrivateKey, keyType: getKeyTypeFromNetwork(network), pkpAddress, }, @@ -59,11 +59,11 @@ export async function batchGeneratePrivateKeys( }); return { - ...(signature ? { signedMessage: { signature } } : {}), - generatedPrivateKey: { - memo: generatedPrivateKey.memo, + ...(signature ? { signMessage: { signature } } : {}), + generateEncryptedPrivateKey: { + memo: generateEncryptedPrivateKey.memo, id, - generatedPublicKey: generatedPrivateKey.publicKey, + generatedPublicKey: generateEncryptedPrivateKey.publicKey, pkpAddress, }, }; diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/batch-generate-keys.ts b/packages/wrapped-keys/src/lib/lit-actions-client/batch-generate-keys.ts index c8f27e73ce..a1a70a0f00 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/batch-generate-keys.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/batch-generate-keys.ts @@ -20,8 +20,8 @@ interface GeneratePrivateKeyLitActionResult { interface BatchGeneratePrivateKeysWithLitActionResult { network: Network; - signedMessage?: { signature: string }; - generatedPrivateKey: GeneratePrivateKeyLitActionResult; + signMessage?: { signature: string }; + generateEncryptedPrivateKey: GeneratePrivateKeyLitActionResult; } export async function batchGenerateKeysWithLitAction( diff --git a/packages/wrapped-keys/src/lib/types.ts b/packages/wrapped-keys/src/lib/types.ts index b86a5b047f..ab03175543 100644 --- a/packages/wrapped-keys/src/lib/types.ts +++ b/packages/wrapped-keys/src/lib/types.ts @@ -153,8 +153,8 @@ export type BatchGeneratePrivateKeysParams = BaseApiParams & { * @extends ApiParamsSupportedNetworks */ export interface BatchGeneratePrivateKeysActionResult { - generatedPrivateKey: GeneratePrivateKeyResult & { memo: string }; - signedMessage?: { signature: string }; + generateEncryptedPrivateKey: GeneratePrivateKeyResult & { memo: string }; + signMessage?: { signature: string }; } export interface BatchGeneratePrivateKeysResult { From f79e3dc0043b35d3be9e115564f950c92c6ddc0c Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 23:17:30 +0100 Subject: [PATCH 46/53] chore(wrapped-keys-lit-actions): LIT-3920 - Update `signTransactionWithEncryptedSolanaKey` LA to validate unsigned tx before decrypting the private key --- local-tests/test.ts | 32 +++++++++---------- .../lib/solana/internal/signTransaction.js | 4 +-- .../signTransactionWithEncryptedSolanaKey.js | 7 +++- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/local-tests/test.ts b/local-tests/test.ts index 431460344b..a7adc12858 100644 --- a/local-tests/test.ts +++ b/local-tests/test.ts @@ -291,22 +291,22 @@ setLitActionsCodeToLocal(); tests: { // testExample, // testBundleSpeed, - ...eoaSessionSigsTests, - ...pkpSessionSigsTests, - ...litActionSessionSigsTests, - ...litActionIpfsIdSessionSigsTests, - ...capacityDelegationTests, - ...bareAuthSigTests, - - ...pkpEthersTest.eoaSessionSigs, - ...pkpEthersTest.pkpSessionSigs, - ...pkpEthersTest.litActionSessionSigs, - - ...litActionCombiningTests.broadcastAndCombine, - ...litActionCombiningTests.decryptAndCombine, - ...litActionCombiningTests.ecdsaSignAndCombine, - - ...relayerTests, + // ...eoaSessionSigsTests, + // ...pkpSessionSigsTests, + // ...litActionSessionSigsTests, + // ...litActionIpfsIdSessionSigsTests, + // ...capacityDelegationTests, + // ...bareAuthSigTests, + // + // ...pkpEthersTest.eoaSessionSigs, + // ...pkpEthersTest.pkpSessionSigs, + // ...pkpEthersTest.litActionSessionSigs, + // + // ...litActionCombiningTests.broadcastAndCombine, + // ...litActionCombiningTests.decryptAndCombine, + // ...litActionCombiningTests.ecdsaSignAndCombine, + // + // ...relayerTests, ...wrappedKeysTests, }, devEnv, diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransaction.js b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransaction.js index 0c487ff124..c944426673 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransaction.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/internal/signTransaction.js @@ -7,7 +7,7 @@ const { /* global ethers */ -function validateUnsignedTransaction(unsignedTransaction) { +export function validateUnsignedTransaction(unsignedTransaction) { const VALID_NETWORKS = ['devnet', 'testnet', 'mainnet-beta']; if (!VALID_NETWORKS.includes(unsignedTransaction.chain)) { @@ -48,7 +48,7 @@ export async function signTransactionSolanaKey({ privateKey, unsignedTransaction, }) { - validateUnsignedTransaction(unsignedTransaction); + // Be sure you call validateUnsignedTransaction(unsignedTransaction); before calling this method! const solanaKeyPair = Keypair.fromSecretKey(Buffer.from(privateKey, 'hex')); diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js index 72d3820ad1..5a243e9c42 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/signTransactionWithEncryptedSolanaKey.js @@ -1,4 +1,7 @@ -const { signTransactionSolanaKey } = require('./internal/signTransaction'); +const { + signTransactionSolanaKey, + validateUnsignedTransaction, +} = require('./internal/signTransaction'); const { getDecryptedKeyToSingleNode, } = require('../common/internal/getDecryptedKeyToSingleNode'); @@ -22,6 +25,8 @@ const { removeSaltFromDecryptedKey } = require('../utils'); (async () => { try { + validateUnsignedTransaction(unsignedTransaction); + const decryptedPrivateKey = await getDecryptedKeyToSingleNode({ accessControlConditions, ciphertext, From 2175a8ce6ef37eec65d593381d60e7b0c193446b Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 23:25:21 +0100 Subject: [PATCH 47/53] chore(wrapped-keys-lit-actions): LIT-3920 - Update `signTransactionWithEncryptedEthereumKey` LA to validate unsigned tx before decrypting the private key --- .../lib/ethereum/internal/signTransaction.js | 25 +++++++++---------- ...signTransactionWithEncryptedEthereumKey.js | 8 +++++- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransaction.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransaction.js index 103144525f..192f133692 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransaction.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/internal/signTransaction.js @@ -1,6 +1,6 @@ /* global ethers, Lit */ -function getValidatedUnsignedTx(unsignedTransaction) { +export function getValidatedUnsignedTx(unsignedTransaction) { try { if (!unsignedTransaction.toAddress) { throw new Error('Missing required field: toAddress'); @@ -68,21 +68,21 @@ async function getGasPrice({ userProvidedGasPrice, provider }) { } } -async function getGasLimit({ provider, userProvidedGasLimit, tx }) { +async function getGasLimit({ provider, userProvidedGasLimit, validatedTx }) { if (userProvidedGasLimit) { return userProvidedGasLimit; } else { try { - return await provider.estimateGas(tx); + return await provider.estimateGas(validatedTx); } catch (err) { throw new Error(`When estimating gas - ${err.message}`); } } } -async function signTransaction({ tx, wallet }) { +async function signTransaction({ validatedTx, wallet }) { try { - return await wallet.signTransaction(tx); + return await wallet.signTransaction(validatedTx); } catch (err) { throw new Error(`When signing transaction - ${err.message}`); } @@ -99,13 +99,12 @@ async function broadcastTransaction({ provider, signedTx }) { export async function signTransactionEthereumKey({ broadcast, privateKey, + validatedTx, unsignedTransaction, }) { - const tx = getValidatedUnsignedTx(unsignedTransaction); - const wallet = new ethers.Wallet(privateKey); - tx.from = wallet.address; + validatedTx.from = wallet.address; const [nonce, provider] = await Promise.all([ getLatestNonce({ @@ -117,20 +116,20 @@ export async function signTransactionEthereumKey({ }), ]); - tx.nonce = nonce; + validatedTx.nonce = nonce; - tx.gasPrice = await getGasPrice({ + validatedTx.gasPrice = await getGasPrice({ provider, userProvidedGasPrice: unsignedTransaction.gasPrice, }); - tx.gasLimit = await getGasLimit({ + validatedTx.gasLimit = await getGasLimit({ provider, - tx, + validatedTx, userProvidedGasLimit: unsignedTransaction.gasLimit, }); - const signedTx = await signTransaction({ tx, wallet }); + const signedTx = await signTransaction({ validatedTx, wallet }); if (!broadcast) { return signedTx; diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js index cddaf08386..2f35f19583 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/signTransactionWithEncryptedEthereumKey.js @@ -1,4 +1,7 @@ -const { signTransactionEthereumKey } = require('./internal/signTransaction'); +const { + signTransactionEthereumKey, + getValidatedUnsignedTx, +} = require('./internal/signTransaction'); const { getDecryptedKeyToSingleNode, } = require('../common/internal/getDecryptedKeyToSingleNode'); @@ -21,6 +24,8 @@ const { removeSaltFromDecryptedKey } = require('../utils'); */ (async () => { try { + const validatedTx = getValidatedUnsignedTx(unsignedTransaction); + const decryptedPrivateKey = await getDecryptedKeyToSingleNode({ accessControlConditions, ciphertext, @@ -38,6 +43,7 @@ const { removeSaltFromDecryptedKey } = require('../utils'); broadcast, privateKey, unsignedTransaction, + validatedTx, }); Lit.Actions.setResponse({ response: txResult }); From f5a9652673b71dfd22d535311e4f7b797143a17c Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Wed, 9 Oct 2024 23:28:50 +0100 Subject: [PATCH 48/53] chore(wrapped-keys-lit-actions): LIT-3920 - Fix accidental comment :) --- local-tests/test.ts | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/local-tests/test.ts b/local-tests/test.ts index a7adc12858..431460344b 100644 --- a/local-tests/test.ts +++ b/local-tests/test.ts @@ -291,22 +291,22 @@ setLitActionsCodeToLocal(); tests: { // testExample, // testBundleSpeed, - // ...eoaSessionSigsTests, - // ...pkpSessionSigsTests, - // ...litActionSessionSigsTests, - // ...litActionIpfsIdSessionSigsTests, - // ...capacityDelegationTests, - // ...bareAuthSigTests, - // - // ...pkpEthersTest.eoaSessionSigs, - // ...pkpEthersTest.pkpSessionSigs, - // ...pkpEthersTest.litActionSessionSigs, - // - // ...litActionCombiningTests.broadcastAndCombine, - // ...litActionCombiningTests.decryptAndCombine, - // ...litActionCombiningTests.ecdsaSignAndCombine, - // - // ...relayerTests, + ...eoaSessionSigsTests, + ...pkpSessionSigsTests, + ...litActionSessionSigsTests, + ...litActionIpfsIdSessionSigsTests, + ...capacityDelegationTests, + ...bareAuthSigTests, + + ...pkpEthersTest.eoaSessionSigs, + ...pkpEthersTest.pkpSessionSigs, + ...pkpEthersTest.litActionSessionSigs, + + ...litActionCombiningTests.broadcastAndCombine, + ...litActionCombiningTests.decryptAndCombine, + ...litActionCombiningTests.ecdsaSignAndCombine, + + ...relayerTests, ...wrappedKeysTests, }, devEnv, From 1204c8de5cb7721690240c59d63d578c450bb852 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Thu, 10 Oct 2024 00:52:26 +0100 Subject: [PATCH 49/53] feat(lit-node-client-nodejs): LIT-3920 - Replace `numResponsesRequired` with `useSingleNode` - Fixed test assertions in testUseEoaSessionSigsToRequestSingleResponse --- ...seEoaSessionSigsToRequestSingleResponse.ts | 59 ++++++++++--------- packages/core/src/lib/lit-core.ts | 12 ++++ .../src/lib/lit-node-client-nodejs.ts | 56 +++++++++++------- packages/types/src/lib/interfaces.ts | 9 +-- .../lit-actions-client/batch-generate-keys.ts | 2 +- 5 files changed, 81 insertions(+), 57 deletions(-) diff --git a/local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts b/local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts index c2b10f4ac7..69583ea7c6 100644 --- a/local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts +++ b/local-tests/tests/testUseEoaSessionSigsToRequestSingleResponse.ts @@ -20,37 +20,38 @@ export const testUseEoaSessionSigsToRequestSingleResponse = async ( code: `(async () => { console.log('hello world') })();`, - numResponsesRequired: 1, + useSingleNode: true, }); + + console.log('res:', res); + + // Expected output: + // { + // success: true, + // signedData: {}, + // decryptedData: {}, + // claimData: {}, + // response: "", + // logs: "hello world\n", + // } + + // -- assertions + if (res.response) { + throw new Error(`Expected "response" to be falsy`); + } + + if (!res.logs) { + throw new Error(`Expected "logs" in res`); + } + + if (!res.logs.includes('hello world')) { + throw new Error(`Expected "logs" to include 'hello world'`); + } + + if (!res.success) { + throw new Error(`Expected "success" in res`); + } } finally { devEnv.releasePrivateKeyFromUser(alice); } - console.log('res:', res); - - // Expected output: - // { - // success: true, - // signedData: {}, - // decryptedData: {}, - // claimData: {}, - // response: "", - // logs: "hello world\n", - // } - - // -- assertions - if (res.response) { - throw new Error(`Expected "response" to be falsy`); - } - - if (!res.logs) { - throw new Error(`Expected "logs" in res`); - } - - if (!res.logs.includes('hello world')) { - throw new Error(`Expected "logs" to include 'hello world'`); - } - - if (!res.success) { - throw new Error(`Expected "success" in res`); - } }; diff --git a/packages/core/src/lib/lit-core.ts b/packages/core/src/lib/lit-core.ts index 3bc7a0f24f..51b267c1a6 100644 --- a/packages/core/src/lib/lit-core.ts +++ b/packages/core/src/lib/lit-core.ts @@ -1068,6 +1068,18 @@ export class LitCore { return nodePromises; }; + getRandomNodePromise( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + callback: (url: string) => Promise + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ): Promise[] { + const randomNodeIndex = Math.floor( + Math.random() * this.connectedNodes.size + ); + + const nodeUrlsArr = Array.from(this.connectedNodes); + return [callback(nodeUrlsArr[randomNodeIndex])]; + } /** * Retrieves the session signature for a given URL from the sessionSigs map. * Throws an error if sessionSigs is not provided or if the session signature for the URL is not found. diff --git a/packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts b/packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts index c3dff30504..858a46d065 100644 --- a/packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts +++ b/packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts @@ -996,6 +996,29 @@ export class LitNodeClientNodeJs throw new Error('All IPFS gateways failed to fetch the code.'); } + private async executeJsNodeRequest( + url: string, + formattedParams: JsonExecutionSdkParams, + requestId: string + ) { + // -- choose the right signature + const sessionSig = this.getSessionSigByUrl({ + sessionSigs: formattedParams.sessionSigs, + url, + }); + + const reqBody: JsonExecutionRequest = { + ...formattedParams, + authSig: sessionSig, + }; + + const urlWithPath = composeLitUrl({ + url, + endpoint: LIT_ENDPOINT.EXECUTE_JS, + }); + + return this.generatePromise(urlWithPath, reqBody, requestId); + } /** * * Execute JS on the nodes and combine and return any resulting signatures @@ -1074,31 +1097,24 @@ export class LitNodeClientNodeJs const requestId = this.getRequestId(); // ========== Get Node Promises ========== // Handle promises for commands sent to Lit nodes - const nodePromises = this.getNodePromises(async (url: string) => { - // -- choose the right signature - const sessionSig = this.getSessionSigByUrl({ - sessionSigs: formattedParams.sessionSigs, - url, - }); - - const reqBody: JsonExecutionRequest = { - ...formattedParams, - authSig: sessionSig, - }; - - const urlWithPath = composeLitUrl({ - url, - endpoint: LIT_ENDPOINT.EXECUTE_JS, - }); + const getNodePromises = async () => { + if (params.useSingleNode) { + return this.getRandomNodePromise((url: string) => + this.executeJsNodeRequest(url, formattedParams, requestId) + ); + } + return this.getNodePromises((url: string) => + this.executeJsNodeRequest(url, formattedParams, requestId) + ); + }; - return this.generatePromise(urlWithPath, reqBody, requestId); - }); + const nodePromises = await getNodePromises(); // -- resolve promises const res = await this.handleNodePromises( nodePromises, requestId, - params.numResponsesRequired || this.connectedNodes.size + params.useSingleNode ? 1 : this.connectedNodes.size ); // -- case: promises rejected @@ -1162,7 +1178,7 @@ export class LitNodeClientNodeJs const signatures = getSignatures({ requestId, networkPubKeySet: this.networkPubKeySet, - minNodeCount: params.numResponsesRequired || this.config.minNodeCount, + minNodeCount: params.useSingleNode ? 1 : this.config.minNodeCount, signedData: signedDataList, }); diff --git a/packages/types/src/lib/interfaces.ts b/packages/types/src/lib/interfaces.ts index b626bd708f..d44c6162cc 100644 --- a/packages/types/src/lib/interfaces.ts +++ b/packages/types/src/lib/interfaces.ts @@ -538,14 +538,9 @@ export interface ExecuteJsAdvancedOptions { ipfsOptions?: IpfsOptions; /** - * number of responses required to consider the execution successful + * Only run the action on a single node; this will only work if all code in your action is non-interactive */ - numResponsesRequired?: number; - - /** - * idea: the number of nodes to pay for running executions - */ - // numNodesToRunOn?: number; + useSingleNode?: boolean; } export interface JsonExecutionRequestTargetNode extends JsonExecutionRequest { diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/batch-generate-keys.ts b/packages/wrapped-keys/src/lib/lit-actions-client/batch-generate-keys.ts index a1a70a0f00..4a041da3ad 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/batch-generate-keys.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/batch-generate-keys.ts @@ -37,7 +37,7 @@ export async function batchGenerateKeysWithLitAction( } = args; const result = await litNodeClient.executeJs({ - numResponsesRequired: 1, + useSingleNode: true, sessionSigs: pkpSessionSigs, ipfsId: litActionIpfsCid, code: litActionCode, From 42fd975215ef823ab99463618c6a396cf254a4d1 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Thu, 10 Oct 2024 01:01:40 +0100 Subject: [PATCH 50/53] chore(wrapped-keys-lit-actions): LIT-3920 - Remove `runOnce` and use `useSingleNode` for generating network-specific wrapped keys --- .../generateEncryptedEthereumPrivateKey.js | 21 +++++++------------ .../generateEncryptedSolanaPrivateKey.js | 21 +++++++------------ .../src/lib/lit-actions-client/constants.ts | 8 +++---- .../lib/lit-actions-client/generate-key.ts | 1 + 4 files changed, 19 insertions(+), 32 deletions(-) diff --git a/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js index c6a82e3832..2d6bc44f21 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/ethereum/generateEncryptedEthereumPrivateKey.js @@ -13,21 +13,14 @@ import { generateEthereumPrivateKey } from './internal/generatePrivateKey'; import { encryptPrivateKey } from '../common/internal/encryptKey'; (async () => { - const encryptedKeyResultStr = await Lit.Actions.runOnce( - { waitForResponse: true, name: 'encryptEthereumPrivateKey' }, - async () => { - const { privateKey, publicKey } = generateEthereumPrivateKey(); - return JSON.stringify( - await encryptPrivateKey({ - accessControlConditions, - privateKey, - publicKey, - }) - ); - } - ); + const { privateKey, publicKey } = generateEthereumPrivateKey(); + const encryptedKeyResult = await encryptPrivateKey({ + accessControlConditions, + privateKey, + publicKey, + }); Lit.Actions.setResponse({ - response: encryptedKeyResultStr, + response: JSON.stringify(encryptedKeyResult), }); })(); diff --git a/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js b/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js index 23f288c52d..613dd373d3 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js +++ b/packages/wrapped-keys-lit-actions/src/lib/solana/generateEncryptedSolanaPrivateKey.js @@ -12,21 +12,14 @@ const { encryptPrivateKey } = require('../common/internal/encryptKey'); * @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 encryptedKeyResultStr = await Lit.Actions.runOnce( - { waitForResponse: true, name: 'encryptSolanaPrivateKey' }, - async () => { - const { privateKey, publicKey } = generateSolanaPrivateKey(); - return JSON.stringify( - await encryptPrivateKey({ - accessControlConditions, - publicKey, - privateKey, - }) - ); - } - ); + const { privateKey, publicKey } = generateSolanaPrivateKey(); + const encryptedKeyResult = await encryptPrivateKey({ + accessControlConditions, + publicKey, + privateKey, + }); Lit.Actions.setResponse({ - response: encryptedKeyResultStr, + response: JSON.stringify(encryptedKeyResult), }); })(); diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/constants.ts b/packages/wrapped-keys/src/lib/lit-actions-client/constants.ts index 27e8454950..4a13cb0665 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/constants.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/constants.ts @@ -2,8 +2,8 @@ import { LitCidRepository, LitCidRepositoryCommon } from './types'; const LIT_ACTION_CID_REPOSITORY: LitCidRepository = Object.freeze({ signTransaction: Object.freeze({ - evm: 'QmSr9TLHUFcGMrQQrNXRsYxcgaBFtcCqWi4sgbevbhmrhv', - solana: 'QmS223QiJkPfncUhY2HGQmme7LRgg93NBy8tU1fgGFUUzu', + evm: 'QmRpAgGKEmgeBRhqdC2EH17QUt6puwsbm8Z2nNneVN4uJG', + solana: 'QmR1nPG2tnmC72zuCEMZUZrrMEkbDiMPNHW45Dsm2n7xnk', }), signMessage: Object.freeze({ evm: 'QmXi9iqJvXrHoUGSo5WREonrruDhzQ7cFr7Cry3wX2hmue', @@ -14,13 +14,13 @@ const LIT_ACTION_CID_REPOSITORY: LitCidRepository = Object.freeze({ solana: 'QmYrmzhh7ZoK8LoZ854DmfdRacmZgpVz9c3kW6u5htcdc5', }), exportPrivateKey: Object.freeze({ - solana: 'QmUJ74pTUqeeHzDGdfwCph1vJVNJ1rRzJdvMiTjS1BMwYj', evm: 'QmUJ74pTUqeeHzDGdfwCph1vJVNJ1rRzJdvMiTjS1BMwYj', + solana: 'QmUJ74pTUqeeHzDGdfwCph1vJVNJ1rRzJdvMiTjS1BMwYj', }), }); const LIT_ACTION_CID_REPOSITORY_COMMON: LitCidRepositoryCommon = Object.freeze({ - batchGenerateEncryptedKeys: 'QmTMCnEd5M45EVSBvayc8gAvFsNYRnPjf5sfbtR44bWt68', + batchGenerateEncryptedKeys: 'QmR8Zs7ctSEctxBrSnAYhMXFXCC1ub8K1xvMn5Js3NCSAA', }); export { LIT_ACTION_CID_REPOSITORY, LIT_ACTION_CID_REPOSITORY_COMMON }; diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/generate-key.ts b/packages/wrapped-keys/src/lib/lit-actions-client/generate-key.ts index 02b36c767e..3cfc8253b2 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/generate-key.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/generate-key.ts @@ -26,6 +26,7 @@ export async function generateKeyWithLitAction({ pkpAddress, }: GeneratePrivateKeyLitActionParams): Promise { const result = await litNodeClient.executeJs({ + useSingleNode: true, sessionSigs: pkpSessionSigs, ipfsId: litActionIpfsCid, code: litActionCode, From 84af943c40cb96dd91928afcac73be787d832a0d Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Thu, 10 Oct 2024 01:04:53 +0100 Subject: [PATCH 51/53] chore(wrapped-keys): LIT-3920 - Update LIT_ACTION_CID_REPOSITORY CIDs for `generateEncryptedKey` actions --- packages/wrapped-keys/src/lib/lit-actions-client/constants.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/constants.ts b/packages/wrapped-keys/src/lib/lit-actions-client/constants.ts index 4a13cb0665..83360f84fc 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/constants.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/constants.ts @@ -10,8 +10,8 @@ const LIT_ACTION_CID_REPOSITORY: LitCidRepository = Object.freeze({ solana: 'QmcEJGVqRYtVukjm2prCPT7Fs66GpaqZwmZoxEHMHor6Jz', }), generateEncryptedKey: Object.freeze({ - evm: 'QmPzycC9bN4QfhVWy7ggL4u9Gr1vQz3Gx4MYh4vvpvqBzc', - solana: 'QmYrmzhh7ZoK8LoZ854DmfdRacmZgpVz9c3kW6u5htcdc5', + evm: 'QmeD6NYCWhUCLgxgpkgSguaKjwnpCnJ6Yf8SdsyPpK4eKK', + solana: 'QmPkVD3hEjMi1T5zQuvdrFCXaGTEMHNdAhAL4WHkqxijrQ', }), exportPrivateKey: Object.freeze({ evm: 'QmUJ74pTUqeeHzDGdfwCph1vJVNJ1rRzJdvMiTjS1BMwYj', From 7f92dcfc2c371bf41844cb15098a01068cc300dd Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Thu, 10 Oct 2024 01:15:49 +0100 Subject: [PATCH 52/53] test(wrapped-keys-lit-actions): LIT-3920 - Use ethers.utils.computeAddress to get walletAddress instead of exporting the key in testBatchGeneratePrivateKeys --- .../wrapped-keys/testBatchGeneratePrivateKeys.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts b/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts index 8a2aefba76..7a650855a4 100644 --- a/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts +++ b/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts @@ -49,23 +49,18 @@ async function verifyEvmSignature( } } - const { decryptedPrivateKey } = await exportPrivateKey({ - litNodeClient, - network: 'evm', - id: evmResult.generateEncryptedPrivateKey.id, - pkpSessionSigs, - }); + const walletAddress = ethers.utils.computeAddress( + evmResult.generateEncryptedPrivateKey.generatedPublicKey + ); const recoveredAddress = verifyMessageSignature(); - const wallet = new ethers.Wallet(decryptedPrivateKey); - console.log({ recoveredAddress, - walletAddress: wallet.address, + walletAddress, signature: evmResult.signMessage.signature, }); - if (recoveredAddress !== wallet.address) { + if (recoveredAddress !== walletAddress) { throw new Error( "Recovered address from verifyMessage doesn't match the wallet address" ); From 22747c8bf7c51beb543896d143d5a1365a0bb132 Mon Sep 17 00:00:00 2001 From: Daryl Collins Date: Thu, 10 Oct 2024 01:20:27 +0100 Subject: [PATCH 53/53] test(wrapped-keys-lit-actions): LIT-3920 - Remove unused function args --- .../wrapped-keys/testBatchGeneratePrivateKeys.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts b/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts index 7a650855a4..698ef23919 100644 --- a/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts +++ b/local-tests/tests/wrapped-keys/testBatchGeneratePrivateKeys.ts @@ -30,12 +30,7 @@ async function verifySolanaSignature( ); } } -async function verifyEvmSignature( - litNodeClient, - evmResult, - messageToSign, - pkpSessionSigs -) { +async function verifyEvmSignature(evmResult, messageToSign) { function verifyMessageSignature() { try { return ethers.utils.verifyMessage( @@ -131,12 +126,7 @@ export const testBatchGeneratePrivateKeys = async ( await verifySolanaSignature(results[1], solanaMessageToSign); console.log('evm verify sig'); - await verifyEvmSignature( - devEnv.litNodeClient, - results[0], - evmMessageToSign, - pkpSessionSigsSigning - ); + await verifyEvmSignature(results[0], evmMessageToSign); console.log('results', results); log('✅ testBatchGenerateEncryptedKeys');