-
Notifications
You must be signed in to change notification settings - Fork 88
LIT-3920 - wrapped-keys - Batch generate private keys & optionally sign messages in a single operation #675
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 47 commits
0b40f47
b939e6d
72864d2
7dafaf2
d928051
9f9a670
a294bc4
231ac2b
65b3a63
a72e159
c294587
fe9b60b
d73b7b4
4f632f7
e830162
f7de6b6
d5da0e3
610d2ef
4d34fb1
ca76c7b
13269e7
442dae8
35b33d5
13f0556
8f9f029
90a43cd
f0ceff6
e269157
c3869d4
82de82c
2d47564
f5bdd3f
4874adc
ea9c087
8d517d8
15337ad
7db654f
bae0338
d664472
77cefc2
9a3af57
7f7b387
8e6b8cd
54053fe
765b437
f79e3dc
2175a8c
f5a9652
1204c8d
42fd975
84af943
7f92dcf
22747c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
MaximusHaximus marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -104,6 +104,13 @@ 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 { 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(); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes our tinny tests use the locally built code for all LIT actions in wrapped-keys instead of running them against the CIDs in
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the idea is that users use the IPFS Cid shouldn't the tests reflect that?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess it depends on the users; some people will be using the code directly, others the CIDs... what I want to do is run the entire suite against the cids and then again against the current code ideally, but see other comment re: doing so without jest. The reason this is here right now is because we have a chicken and egg problem - in-progress code in PRs isn't in IPFS, and pinning repeatedly to IPFS as we iterate on a lit action would result in a ton of churn and manual updates right now. We could make this code always use IPFS CIDs, but it would require that all actions we want to test would exist in IPFS before we can test against them -- and we want to run tests while developing the actions where they will be changing often. In this PR, I wanted to be sure tests pass with the current code rather than previous CIDs -- once we have confirmed all of the LIT actions code here is GTG and published all of the lit actions to IPFS, I can update the CIDs in
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool as you mentioned we can run the tests against IPFS afterwards
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should stick to the LA code, not the IPFS. That way we can validate the code with our tests continuously, not depend on IPFS and we can always calculate the CID from the code but not the other way around
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 I agree @FedericoAmura <3 Ideally, all tests would run against the data in the same commit by default, and if we want to test against prior versions we'd explicitly do that. The main thing is just getting the CID repo in the same commit to always match the current source code in that commit... I think we can do that with a husky hook script that runs on-commit
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Good idea |
||
|
|
||
| (async () => { | ||
| console.log('[𐬺🧪 Tinny𐬺] Running tests...'); | ||
|
|
@@ -118,6 +125,7 @@ import { testSignTransactionWithSolanaEncryptedKey } from './tests/wrapped-keys/ | |
| // --filter=WrappedKey | ||
| const wrappedKeysTests = { | ||
| // -- valid cases | ||
| testBatchGeneratePrivateKeys, | ||
| testEthereumSignMessageGeneratedKey, | ||
| testEthereumBroadcastTransactionGeneratedKey, | ||
| testEthereumSignMessageWrappedKey, | ||
|
|
@@ -163,6 +171,7 @@ import { testSignTransactionWithSolanaEncryptedKey } from './tests/wrapped-keys/ | |
| testUseEoaSessionSigsToEncryptDecryptString, | ||
| testUseEoaSessionSigsToEncryptDecryptFile, | ||
| testUseEoaSessionSigsToEncryptDecryptZip, | ||
| testUseEoaSessionSigsToRequestSingleResponse, | ||
| }; | ||
|
|
||
| const pkpSessionSigsTests = { | ||
|
|
@@ -282,22 +291,22 @@ import { testSignTransactionWithSolanaEncryptedKey } from './tests/wrapped-keys/ | |
| 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, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| 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(); | ||
|
|
||
| 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: | ||
| // { | ||
| // 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`); | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| 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, exportPrivateKey } = api; | ||
|
|
||
| async function verifySolanaSignature( | ||
| solanaResult: BatchGeneratePrivateKeysActionResult, | ||
| solanaMessageToSign | ||
| ) { | ||
| const { | ||
| signMessage: { signature }, | ||
| generateEncryptedPrivateKey: { 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.signMessage.signature | ||
| ); | ||
| } catch (err) { | ||
| throw new Error( | ||
| `When validating signed Ethereum message is valid: ${err.message}` | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| const { decryptedPrivateKey } = await exportPrivateKey({ | ||
| litNodeClient, | ||
| network: 'evm', | ||
| id: evmResult.generateEncryptedPrivateKey.id, | ||
| pkpSessionSigs, | ||
| }); | ||
|
|
||
| const recoveredAddress = verifyMessageSignature(); | ||
|
|
||
| const wallet = new ethers.Wallet(decryptedPrivateKey); | ||
|
|
||
| console.log({ | ||
| recoveredAddress, | ||
| walletAddress: wallet.address, | ||
| signature: evmResult.signMessage.signature, | ||
| }); | ||
| if (recoveredAddress !== wallet.address) { | ||
| throw new Error( | ||
| "Recovered address from verifyMessage doesn't match the wallet address" | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Test Commands: | ||
| * ✅ 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 ( | ||
| 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 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: evmMessageToSign }, | ||
| generateKeyParams: { memo: 'Test evm key' }, | ||
| }, | ||
| { | ||
| network: 'solana', | ||
| signMessageParams: { messageToSign: solanaMessageToSign }, | ||
| 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].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' | ||
| ); | ||
| } | ||
|
|
||
| if ( | ||
MaximusHaximus marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| !results[0].signMessage.signature || | ||
| !results[1].signMessage.signature | ||
| ) { | ||
| 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 | ||
MaximusHaximus marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ); | ||
| console.log('results', results); | ||
|
|
||
| log('✅ testBatchGenerateEncryptedKeys'); | ||
| } catch (err) { | ||
| console.log(err.message, err, err.stack); | ||
| throw err; | ||
| } finally { | ||
| devEnv.releasePrivateKeyFromUser(alice); | ||
| } | ||
| }; | ||
Uh oh!
There was an error while loading. Please reload this page.