UNPKG

@lit-protocol/e2e

Version:

Lit Protocol E2E testing package for running comprehensive integration tests

285 lines (283 loc) 11.1 kB
/** * Naga Health Manager * * This module implements health checks for Naga network endpoints. * It tests the core functionality of the Lit Protocol network by executing * a series of endpoint tests using a single test account. * * Tested Endpoints: * 1. Handshake - Verifies basic node connectivity * 2. PKP Sign - Tests PKP signing functionality * 3. Sign Session Key - Tests session key signing (via PKP auth context creation) * 4. Execute JS - Tests Lit Actions execution * 5. Decrypt - Tests encryption and decryption flow * * Usage: * const manager = new NagaHealthManager(ctx); * await manager.handshakeTest(); * await manager.pkpSignTest(); * // ... other tests */ import { createAccBuilder } from '@lit-protocol/access-control-conditions'; import { generateSessionKeyPair } from '@lit-protocol/auth'; import { api as wrappedKeysApi, config as wrappedKeysConfig, } from '@lit-protocol/wrapped-keys'; import { litActionRepository, litActionRepositoryCommon, } from '@lit-protocol/wrapped-keys-lit-actions'; import { Wallet } from 'ethers'; export class NagaHealthManager { ctx; constructor(ctx) { this.ctx = ctx; } /** * Test 1: Handshake Test * * Verifies basic connectivity to Lit nodes by checking if the client * is properly initialized and connected. * * This is the most basic health check - if this fails, the network is down. */ handshakeTest = async () => { try { // Fetch current context which includes the latest handshake result const ctx = await this.ctx.litClient.getContext(); if (!ctx?.handshakeResult) { throw new Error('Handshake result missing from client context'); } const { serverKeys, connectedNodes, threshold } = ctx.handshakeResult; const numServers = serverKeys ? Object.keys(serverKeys).length : 0; const numConnected = connectedNodes ? connectedNodes.size : 0; if (numServers === 0) { throw new Error('No server keys received during handshake'); } if (typeof threshold === 'number' && numConnected < threshold) { throw new Error(`Connected nodes (${numConnected}) below threshold (${threshold})`); } console.log('✅ Handshake test passed'); } catch (e) { console.error('❌ Handshake test failed:', e); throw e; } }; /** * Test 2: PKP Sign Test * * Tests the PKP signing endpoint by signing a simple message. * This verifies that: * - The PKP is accessible * - The auth context is valid * - The signing endpoint is operational */ pkpSignTest = async () => { try { const testMessage = 'Hello from Naga health check!'; const result = await this.ctx.litClient.chain.ethereum.pkpSign({ authContext: this.ctx.aliceEoaAuthContext, pubKey: this.ctx.aliceViemAccountPkp.pubkey, toSign: testMessage, }); if (!result.signature) { throw new Error('No signature returned from pkpSign'); } console.log('✅ PKP Sign test passed'); } catch (e) { console.error('❌ PKP Sign test failed:', e); throw e; } }; /** * Test 3: Sign Session Key Test * * Tests the session key signing endpoint by creating a PKP auth context. * This involves the signSessionKey endpoint which is critical for * establishing authenticated sessions with PKPs. */ signSessionKeyTest = async () => { try { // Creating a PKP auth context involves calling the signSessionKey endpoint const pkpAuthContext = await this.ctx.authManager.createPkpAuthContext({ authData: this.ctx.aliceViemAccountAuthData, pkpPublicKey: this.ctx.aliceViemAccountPkp.pubkey, authConfig: { resources: [ ['pkp-signing', '*'], ['lit-action-execution', '*'], ], expiration: new Date(Date.now() + 1000 * 60 * 15).toISOString(), // 15 minutes }, litClient: this.ctx.litClient, }); if (!pkpAuthContext) { throw new Error('Failed to create PKP auth context'); } console.log('✅ Sign Session Key test passed'); } catch (e) { console.error('❌ Sign Session Key test failed:', e); throw e; } }; /** * Test 4: Execute JS Test * * Tests Lit Actions execution by running a simple JavaScript code * that performs an ECDSA signature. * * This verifies: * - Lit Actions runtime is operational * - Code execution environment is working * - Signing within actions works */ executeJsTest = async () => { try { const litActionCode = ` (async () => { const { sigName, toSign, publicKey } = jsParams; const { keccak256, arrayify } = ethers.utils; const toSignBytes = new TextEncoder().encode(toSign); const toSignBytes32 = keccak256(toSignBytes); const toSignBytes32Array = arrayify(toSignBytes32); const sigShare = await Lit.Actions.signEcdsa({ toSign: toSignBytes32Array, publicKey, sigName, }); })();`; const result = await this.ctx.litClient.executeJs({ code: litActionCode, authContext: this.ctx.aliceEoaAuthContext, jsParams: { sigName: 'health-check-sig', toSign: 'Health check message', publicKey: this.ctx.aliceViemAccountPkp.pubkey, }, }); if (!result || !result.signatures) { throw new Error('No signatures returned from executeJs'); } console.log('✅ Execute JS test passed'); } catch (e) { console.error('❌ Execute JS test failed:', e); throw e; } }; /** * Test 5: Decrypt Test * * Tests the encryption and decryption flow: * 1. Encrypts data with access control conditions * 2. Decrypts the data using the same account * * This verifies: * - Encryption endpoint works * - Access control condition evaluation works * - Decryption endpoint works * - End-to-end encryption flow is operational */ decryptTest = async () => { try { const testData = 'Secret health check data'; // Create access control conditions for Alice's wallet const builder = createAccBuilder(); const accs = builder .requireWalletOwnership(this.ctx.aliceViemAccount.address) .on('ethereum') .build(); // Encrypt the data const encryptedData = await this.ctx.litClient.encrypt({ dataToEncrypt: testData, unifiedAccessControlConditions: accs, chain: 'ethereum', }); if (!encryptedData.ciphertext || !encryptedData.dataToEncryptHash) { throw new Error('Encryption failed - missing ciphertext or hash'); } // Decrypt the data const decryptedData = await this.ctx.litClient.decrypt({ data: encryptedData, unifiedAccessControlConditions: accs, chain: 'ethereum', authContext: this.ctx.aliceEoaAuthContext, }); if (!decryptedData.convertedData) { throw new Error('Decryption failed - no converted data'); } // Verify the decrypted data matches if (decryptedData.convertedData !== testData) { throw new Error(`Decryption mismatch: expected "${testData}", got "${decryptedData.convertedData}"`); } console.log('✅ Decrypt test passed'); } catch (e) { console.error('❌ Decrypt test failed:', e); throw e; } }; /** * Test 6: Wrapped Keys Service Test * * Validates the wrapped keys service by importing an externally generated key * and immediately exporting it to confirm correct round-trip encryption. */ wrappedKeysTest = async () => { try { wrappedKeysConfig.setLitActionsCode(litActionRepository); wrappedKeysConfig.setLitActionsCodeCommon(litActionRepositoryCommon); const sessionKeyPair = generateSessionKeyPair(); const delegationAuthSig = await this.ctx.authManager.generatePkpDelegationAuthSig({ pkpPublicKey: this.ctx.aliceViemAccountPkp.pubkey, authData: this.ctx.aliceViemAccountAuthData, sessionKeyPair, authConfig: { resources: [ ['pkp-signing', '*'], ['lit-action-execution', '*'], ['access-control-condition-decryption', '*'], ], expiration: new Date(Date.now() + 1000 * 60 * 15).toISOString(), }, litClient: this.ctx.litClient, }); const pkpSessionSigs = await this.ctx.authManager.createPkpSessionSigs({ pkpPublicKey: this.ctx.aliceViemAccountPkp.pubkey, sessionKeyPair, delegationAuthSig, litClient: this.ctx.litClient, }); const wallet = Wallet.createRandom(); const memo = `health-check-import-${Date.now()}`; const importResult = await wrappedKeysApi.importPrivateKey({ pkpSessionSigs, litClient: this.ctx.litClient, privateKey: wallet.privateKey, publicKey: wallet.publicKey, keyType: 'K256', memo, }); if (!importResult.id) { throw new Error('Wrapped key import failed - no key id returned'); } const exportResult = await wrappedKeysApi.exportPrivateKey({ pkpSessionSigs, litClient: this.ctx.litClient, id: importResult.id, network: 'evm', }); if (!exportResult.decryptedPrivateKey) { throw new Error('Wrapped key export failed - missing private key'); } if (exportResult.decryptedPrivateKey.toLowerCase() !== wallet.privateKey.toLowerCase()) { throw new Error('Wrapped key export mismatch'); } console.log('✅ Wrapped Keys test passed'); } catch (e) { console.error('❌ Wrapped Keys test failed:', e); throw e; } }; } //# sourceMappingURL=NagaHealthManager.js.map