From 92b2a8588e5970dc407c965a8798ab1e6d2bb823 Mon Sep 17 00:00:00 2001 From: FedericoAmura Date: Mon, 30 Dec 2024 16:16:20 +0100 Subject: [PATCH 1/9] fix: remove extra lit node client in test --- .../testPkpEthersWithEoaSessionSigsToSignWithAuthContext.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/local-tests/tests/testPkpEthersWithEoaSessionSigsToSignWithAuthContext.ts b/local-tests/tests/testPkpEthersWithEoaSessionSigsToSignWithAuthContext.ts index aca18c4d5..4f817fa82 100644 --- a/local-tests/tests/testPkpEthersWithEoaSessionSigsToSignWithAuthContext.ts +++ b/local-tests/tests/testPkpEthersWithEoaSessionSigsToSignWithAuthContext.ts @@ -24,7 +24,6 @@ export const testPkpEthersWithEoaSessionSigsToSignWithAuthContext = async ( pkpPubKey: alice.pkp.publicKey, litNodeClient: devEnv.litNodeClient, authContext: { - client: devEnv.litNodeClient, getSessionSigsProps: { authNeededCallback: async function ( params: AuthCallbackParams From f207527a9bdd9e9a3d6a4ec45750f3242d8e803f Mon Sep 17 00:00:00 2001 From: FedericoAmura Date: Mon, 30 Dec 2024 16:19:47 +0100 Subject: [PATCH 2/9] feat: some type improvements --- .../src/lib/lit-node-client-nodejs.ts | 7 ++++--- packages/types/src/lib/interfaces.ts | 2 +- 2 files changed, 5 insertions(+), 4 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 d07d82b9f..1a00c1fd6 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 @@ -270,9 +270,10 @@ export class LitNodeClientNodeJs * @param obj - The object to check. * @returns True if the object is of type SessionKeyPair. */ - isSessionKeyPair(obj: any): obj is SessionKeyPair { + isSessionKeyPair(obj: unknown): obj is SessionKeyPair { return ( typeof obj === 'object' && + obj !== null && 'publicKey' in obj && 'secretKey' in obj && typeof obj.publicKey === 'string' && @@ -509,7 +510,7 @@ export class LitNodeClientNodeJs resourceAbilityRequests, }: { authSig: AuthSig; - sessionKeyUri: any; + sessionKeyUri: string; resourceAbilityRequests: LitResourceAbilityRequest[]; }): Promise => { const authSigSiweMessage = new SiweMessage(authSig.signedMessage); @@ -1666,7 +1667,7 @@ export class LitNodeClientNodeJs // ========== Extract shares from response data ========== // -- 1. combine signed data as a list, and get the signatures from it - let curveType = responseData[0]?.curveType; + const curveType = responseData[0]?.curveType; if (curveType === 'ECDSA') { throw new Error( diff --git a/packages/types/src/lib/interfaces.ts b/packages/types/src/lib/interfaces.ts index 24f4def9e..d92948d4b 100644 --- a/packages/types/src/lib/interfaces.ts +++ b/packages/types/src/lib/interfaces.ts @@ -1046,7 +1046,7 @@ export interface CommonGetSessionSigsProps { /** * When this session signature will expire. After this time is up you will need to reauthenticate, generating a new session signature. The default time until expiration is 24 hours. The formatting is an [RFC3339](https://datatracker.ietf.org/doc/html/rfc3339) timestamp. */ - expiration?: any; + expiration?: string; /** * The chain to use for the session signature and sign the session key. This value is almost always `ethereum`. If you're using EVM, this parameter isn't very important. From fb2e99a50a48192ece02a476b6f5ebd6e6873e54 Mon Sep 17 00:00:00 2001 From: FedericoAmura Date: Mon, 30 Dec 2024 16:30:02 +0100 Subject: [PATCH 3/9] feat: when using lit node client before connecting, just connect instead of throwing error --- packages/core/src/lib/lit-core.ts | 7 +++ .../src/lib/lit-node-client-nodejs.ts | 49 ++++--------------- 2 files changed, 16 insertions(+), 40 deletions(-) diff --git a/packages/core/src/lib/lit-core.ts b/packages/core/src/lib/lit-core.ts index 2c23752e0..4a6276905 100644 --- a/packages/core/src/lib/lit-core.ts +++ b/packages/core/src/lib/lit-core.ts @@ -560,6 +560,13 @@ export class LitCore { } } + protected async assertConnected() { + // -- if it's not ready yet, then connect + if (!this.ready) { + await this.connect(); + } + } + private async _handshakeAndVerifyNodeAttestation({ url, requestId, 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 1a00c1fd6..2882592b9 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 @@ -184,10 +184,7 @@ export class LitNodeClientNodeJs await params.dAppOwnerWallet.getAddress() ); - // -- if it's not ready yet, then connect - if (!this.ready) { - await this.connect(); - } + await this.assertConnected(); const siweMessage = await createSiweMessageWithCapacityDelegation({ uri: 'lit:capability:delegation', @@ -890,13 +887,7 @@ export class LitNodeClientNodeJs executeJs = async ( params: JsonExecutionSdkParams ): Promise => { - // ========== Validate Params ========== - if (!this.ready) { - const message = - '[executeJs] LitNodeClient is not ready. Please call await litNodeClient.connect() first.'; - - throw new LitNodeClientNotReadyError({}, message); - } + await this.assertConnected(); const paramsIsSafe = safeParams({ functionName: 'executeJs', @@ -1226,15 +1217,9 @@ export class LitNodeClientNodeJs * @throws { Error } if the subnetPubKey is null */ encrypt = async (params: EncryptSdkParams): Promise => { - // ========== Validate Params ========== - // -- validate if it's ready - if (!this.ready) { - throw new LitNodeClientNotReadyError( - {}, - '6 LitNodeClient is not ready. Please call await litNodeClient.connect() first.' - ); - } + await this.assertConnected(); + // ========== Validate Params ========== // -- validate if this.subnetPubKey is null if (!this.subnetPubKey) { throw new LitNodeClientNotReadyError({}, 'subnetPubKey cannot be null'); @@ -1316,15 +1301,9 @@ export class LitNodeClientNodeJs const { sessionSigs, authSig, chain, ciphertext, dataToEncryptHash } = params; - // ========== Validate Params ========== - // -- validate if it's ready - if (!this.ready) { - throw new LitNodeClientNotReadyError( - {}, - '6 LitNodeClient is not ready. Please call await litNodeClient.connect() first.' - ); - } + await this.assertConnected(); + // ========== Validate Params ========== // -- validate if this.subnetPubKey is null if (!this.subnetPubKey) { throw new LitNodeClientNotReadyError({}, 'subnetPubKey cannot be null'); @@ -1521,15 +1500,9 @@ export class LitNodeClientNodeJs ): Promise => { log(`[signSessionKey] params:`, params); - // ========== Validate Params ========== - // -- validate: If it's NOT ready - if (!this.ready) { - throw new LitNodeClientNotReadyError( - {}, - '[signSessionKey] ]LitNodeClient is not ready. Please call await litNodeClient.connect() first.' - ); - } + await this.assertConnected(); + // ========== Validate Params ========== // -- construct SIWE message that will be signed by node to generate an authSig. const _expiration = params.expiration || @@ -2195,11 +2168,7 @@ export class LitNodeClientNodeJs async claimKeyId( params: ClaimRequest ): Promise { - if (!this.ready) { - const message = - 'LitNodeClient is not ready. Please call await litNodeClient.connect() first.'; - throw new LitNodeClientNotReadyError({}, message); - } + await this.assertConnected(); if (params.authMethod.authMethodType == AUTH_METHOD_TYPE.WebAuthn) { throw new LitNodeClientNotReadyError( From 2916a14abb0026f95afba9c9940c5e3eae370971 Mon Sep 17 00:00:00 2001 From: FedericoAmura Date: Mon, 30 Dec 2024 16:31:44 +0100 Subject: [PATCH 4/9] fix: properly name authSigs and remove old now unsupported authMethods --- .../src/lib/lit-node-client-nodejs.ts | 16 +++++----------- packages/types/src/lib/interfaces.ts | 1 - 2 files changed, 5 insertions(+), 12 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 2882592b9..1d5101840 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 @@ -858,14 +858,14 @@ export class LitNodeClientNodeJs requestId: string ) { // -- choose the right signature - const sessionSig = this.getSessionSigByUrl({ + const authSig = this.getSessionSigByUrl({ sessionSigs: formattedParams.sessionSigs, url, }); const reqBody: JsonExecutionRequest = { ...formattedParams, - authSig: sessionSig, + authSig, }; const urlWithPath = composeLitUrl({ @@ -1136,21 +1136,15 @@ export class LitNodeClientNodeJs const nodePromises = this.getNodePromises((url: string) => { // -- get the session sig from the url key - const sessionSig = this.getSessionSigByUrl({ - sessionSigs: params.sessionSigs, + const authSig = this.getSessionSigByUrl({ + sessionSigs, url, }); const reqBody: JsonPkpSignRequest = { toSign: normalizeArray(params.toSign), pubkey: hexPrefixed(params.pubKey), - authSig: sessionSig, - - // -- optional params - ...(params.authMethods && - params.authMethods.length > 0 && { - authMethods: params.authMethods, - }), + authSig, }; logWithRequestId(requestId, 'reqBody:', reqBody); diff --git a/packages/types/src/lib/interfaces.ts b/packages/types/src/lib/interfaces.ts index d92948d4b..b796efd62 100644 --- a/packages/types/src/lib/interfaces.ts +++ b/packages/types/src/lib/interfaces.ts @@ -233,7 +233,6 @@ pub struct JsonExecutionRequest { */ export interface BaseJsonPkpSignRequest { - authMethods?: AuthMethod[]; toSign: ArrayLike; } From 8bb94c91895bb63338490f4d8b2beb43a94d4210 Mon Sep 17 00:00:00 2001 From: FedericoAmura Date: Mon, 30 Dec 2024 16:35:03 +0100 Subject: [PATCH 5/9] fix: use formattedParams when overriding lit action code instead of ipfs id --- .../src/lib/lit-node-client-nodejs.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 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 1d5101840..985a23f85 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 @@ -924,19 +924,19 @@ export class LitNodeClientNodeJs // Check if IPFS options are provided and if the code should be fetched from IPFS and overwrite the current code. // This will fetch the code from the specified IPFS gateway using the provided ipfsId, - // and update the params with the fetched code, removing the ipfsId afterward. + // and update the formattedParams with the fetched code, removing the ipfsId afterward. const overwriteCode = params.ipfsOptions?.overwriteCode || GLOBAL_OVERWRITE_IPFS_CODE_BY_NETWORK[this.config.litNetwork]; - if (overwriteCode && params.ipfsId) { + if (overwriteCode && formattedParams.ipfsId) { const code = await this._getFallbackIpfsCode( - params.ipfsOptions?.gatewayUrl, - params.ipfsId + formattedParams.ipfsOptions?.gatewayUrl, + formattedParams.ipfsId ); formattedParams = { - ...params, + ...formattedParams, code: code, ipfsId: undefined, }; @@ -946,7 +946,7 @@ export class LitNodeClientNodeJs // ========== Get Node Promises ========== // Handle promises for commands sent to Lit nodes const getNodePromises = async () => { - if (params.useSingleNode) { + if (formattedParams.useSingleNode) { return this.getRandomNodePromise((url: string) => this.executeJsNodeRequest(url, formattedParams, requestId) ); @@ -962,7 +962,7 @@ export class LitNodeClientNodeJs const res = await this.handleNodePromises( nodePromises, requestId, - params.useSingleNode ? 1 : this.connectedNodes.size + formattedParams.useSingleNode ? 1 : this.connectedNodes.size ); // -- case: promises rejected From 5501ac30cd9f71e4fa4bf81a90a792bba19e4a5c Mon Sep 17 00:00:00 2001 From: FedericoAmura Date: Mon, 30 Dec 2024 16:36:36 +0100 Subject: [PATCH 6/9] feat: add support for auth context in lit node client to abstract session sigs on each lit network request --- .../src/lib/lit-node-client-nodejs.ts | 141 ++++++++++++------ packages/types/src/lib/interfaces.ts | 21 ++- 2 files changed, 112 insertions(+), 50 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 985a23f85..f1abbcdb4 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 @@ -30,6 +30,7 @@ import { LitNodeClientNotReadyError, ParamNullError, ParamsMissingError, + UnauthorizedException, UnknownError, UnsupportedMethodError, WalletSignatureNotFoundError, @@ -63,7 +64,11 @@ import { setStorageItem, } from '@lit-protocol/misc-browser'; import { nacl } from '@lit-protocol/nacl'; -import { ILitResource, ISessionCapabilityObject } from '@lit-protocol/types'; +import { + AuthenticationProps, + ILitResource, + ISessionCapabilityObject, +} from '@lit-protocol/types'; import { uint8arrayFromString, uint8arrayToString, @@ -139,6 +144,7 @@ export class LitNodeClientNodeJs implements LitClientSessionManager, ILitNodeClient { defaultAuthCallback?: (authSigParams: AuthCallbackParams) => Promise; + authContext?: AuthenticationProps; // ========== Constructor ========== constructor(args: LitNodeClientConfig | CustomNetwork) { @@ -151,6 +157,30 @@ export class LitNodeClientNodeJs if (args !== undefined && args !== null && 'defaultAuthCallback' in args) { this.defaultAuthCallback = args.defaultAuthCallback; } + + this.setAuthContext(args.authContext); + } + + public setAuthContext(authContext?: AuthenticationProps) { + this.authContext = authContext; + } + + public async getAuthContextSessionSigs( + getSessionSigsPropsOverride?: Partial + ): Promise { + if (!this.authContext) { + throw new UnauthorizedException( + { + info: {}, + }, + 'authContext is not set. Cannot authenticate using it' + ); + } + + return this.getSessionSigs({ + ...this.authContext?.getSessionSigsProps, + ...(getSessionSigsPropsOverride || {}), + }); } // ========== Rate Limit NFT ========== @@ -725,10 +755,15 @@ export class LitNodeClientNodeJs ); } + const sessionSigs = await this._getValidSessionSigs( + params.sessionSigs, + params.getSessionSigsPropsOverride + ); + // determine which node to run on const ipfsId = await this.getIpfsId({ dataToHash: params.code!, - sessionSigs: params.sessionSigs, + sessionSigs, }); // select targetNodeRange number of random index of the bootstrapUrls.length @@ -779,7 +814,7 @@ export class LitNodeClientNodeJs // -- choose the right signature const sessionSig = this.getSessionSigByUrl({ - sessionSigs: params.sessionSigs, + sessionSigs, url, }); @@ -857,6 +892,18 @@ export class LitNodeClientNodeJs formattedParams: JsonExecutionSdkParams, requestId: string ) { + if (!formattedParams.sessionSigs) { + throw new UnauthorizedException( + { + info: { + url, + requestId, + }, + }, + `sessionSigs missing on execute js request` + ); + } + // -- choose the right signature const authSig = this.getSessionSigByUrl({ sessionSigs: formattedParams.sessionSigs, @@ -905,21 +952,18 @@ export class LitNodeClientNodeJs ); } - // validate session sigs - const checkedSessionSigs = validateSessionSigs(params.sessionSigs); - - if (checkedSessionSigs.isValid === false) { - throw new InvalidSessionSigs( - {}, - `Invalid sessionSigs. Errors: ${checkedSessionSigs.errors}` - ); - } + // -- get sessionSigs + const sessionSigs = await this._getValidSessionSigs( + params.sessionSigs, + params.getSessionSigsPropsOverride + ); // Format the params let formattedParams: JsonExecutionSdkParams = { ...params, ...(params.jsParams && { jsParams: normalizeJsParams(params.jsParams) }), ...(params.code && { code: encodeCode(params.code) }), + sessionSigs, }; // Check if IPFS options are provided and if the code should be fetched from IPFS and overwrite the current code. @@ -1082,13 +1126,12 @@ export class LitNodeClientNodeJs * @param { JsonPkpSignSdkParams } params * @param params.toSign - The data to sign * @param params.pubKey - The public key to sign with - * @param params.sessionSigs - The session signatures to use - * @param params.authMethods - (optional) The auth methods to use + * @param [params.sessionSigs] - The session signatures to use + * @param [params.getSessionSigsPropsOverride] - The props override when obtaining sessionSigs from the auth context */ pkpSign = async (params: JsonPkpSignSdkParams): Promise => { // -- validate required params const requiredParamKeys = ['toSign', 'pubKey']; - (requiredParamKeys as (keyof JsonPkpSignSdkParams)[]).forEach((key) => { if (!params[key]) { throw new ParamNullError( @@ -1104,35 +1147,14 @@ export class LitNodeClientNodeJs } }); - // -- validate present of accepted auth methods - if ( - !params.sessionSigs && - (!params.authMethods || params.authMethods.length <= 0) - ) { - throw new ParamNullError( - { - info: { - params, - }, - }, - 'Either sessionSigs or authMethods (length > 0) must be present.' - ); - } - - const requestId = this._getNewRequestId(); - - // validate session sigs - const checkedSessionSigs = validateSessionSigs(params.sessionSigs); - - if (checkedSessionSigs.isValid === false) { - throw new InvalidSessionSigs( - {}, - `Invalid sessionSigs. Errors: ${checkedSessionSigs.errors}` - ); - } - // ========== Get Node Promises ========== + // -- get sessionSigs + const sessionSigs = await this._getValidSessionSigs( + params.sessionSigs, + params.getSessionSigsPropsOverride + ); // Handle promises for commands sent to Lit nodes + const requestId = this._getNewRequestId(); const nodePromises = this.getNodePromises((url: string) => { // -- get the session sig from the url key @@ -1993,6 +2015,41 @@ export class LitNodeClientNodeJs return signatures; }; + private _getValidSessionSigs = async ( + providedSessionSigs?: SessionSigsMap, + getSessionSigsPropsOverride?: Partial + ) => { + const sessionSigs = + providedSessionSigs || + (await this.getAuthContextSessionSigs(getSessionSigsPropsOverride)); + + if (!sessionSigs) { + throw new ParamNullError( + { + info: { + providedSessionSigs, + authContext: this.authContext, + }, + }, + 'Could not get sessionSigs. Must provide them or set an authContext on construction or with setAuthContext' + ); + } + const checkedSessionSigs = validateSessionSigs(sessionSigs); + if (!checkedSessionSigs.isValid) { + throw new InvalidSessionSigs( + { + info: { + sessionSigsValidation: checkedSessionSigs.isValid, + sessionSigsErrors: checkedSessionSigs.errors, + }, + }, + `Invalid sessionSigs. Errors: ${checkedSessionSigs.errors}` + ); + } + + return sessionSigs; + }; + /** * Retrieves the PKP sessionSigs. * diff --git a/packages/types/src/lib/interfaces.ts b/packages/types/src/lib/interfaces.ts index b796efd62..126d968dc 100644 --- a/packages/types/src/lib/interfaces.ts +++ b/packages/types/src/lib/interfaces.ts @@ -186,14 +186,18 @@ export interface LitNodeClientConfig { contractContext?: LitContractContext | LitContractResolverContext; storageProvider?: StorageProvider; defaultAuthCallback?: (authSigParams: AuthCallbackParams) => Promise; + authContext?: AuthenticationProps; rpcUrl?: string; } export type CustomNetwork = Pick< LitNodeClientConfig, - 'litNetwork' | 'contractContext' | 'checkNodeAttestation' -> & - Partial>; + | 'litNetwork' + | 'contractContext' + | 'checkNodeAttestation' + | 'authContext' + | 'minNodeCount' +>; /** * Override for LocalStorage and SessionStorage @@ -238,11 +242,12 @@ export interface BaseJsonPkpSignRequest { /** * The 'pkpSign' function param. Please note that the structure - * is different than the payload sent to the node. + * is different from the payload sent to the node. */ export interface JsonPkpSignSdkParams extends BaseJsonPkpSignRequest { pubKey: string; - sessionSigs: SessionSigsMap; + sessionSigs?: SessionSigsMap; + getSessionSigsPropsOverride?: Partial; } /** @@ -474,12 +479,12 @@ export interface JsonExecutionSdkParams /** * the session signatures to use to authorize the user with the nodes */ - sessionSigs: SessionSigsMap; + sessionSigs?: SessionSigsMap; /** - * auth methods to resolve + * the lit node client auth context props to get session sigs override */ - authMethods?: AuthMethod[]; + getSessionSigsPropsOverride?: Partial; } export interface ExecuteJsAdvancedOptions { From 6e071ea515dbfbdc3d56c2f50e39f2a62ac6fda7 Mon Sep 17 00:00:00 2001 From: FedericoAmura Date: Mon, 30 Dec 2024 19:08:23 +0100 Subject: [PATCH 7/9] feat: add tests to validate using an auth context configured in lit node client instead of passing session sigs generated previously --- .github/workflows/ci.yml | 2 +- local-tests/test.ts | 2 + .../tests/testUseEoaAuthContextToPkpSign.ts | 127 ++++++++++++++++++ 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 local-tests/tests/testUseEoaAuthContextToPkpSign.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a9a2627a..f8dec6bed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,7 +103,7 @@ jobs: run: cp .env.ci .env - name: Run End to End Tests if: steps.build.outputs.exit_code == 0 - run: yarn test:local --filter=testUseEoaSessionSigsToExecuteJsSigning,testUseEoaSessionSigsToPkpSign,testUsePkpSessionSigsToExecuteJsSigning,testUsePkpSessionSigsToPkpSign,testUseValidLitActionCodeGeneratedSessionSigsToPkpSign,testUseValidLitActionCodeGeneratedSessionSigsToExecuteJsSigning,testDelegatingCapacityCreditsNFTToAnotherWalletToExecuteJs,testEthAuthSigToEncryptDecryptString,testExecuteJsSignAndCombineEcdsa,testExecutJsDecryptAndCombine,testExecuteJsBroadcastAndCollect --exclude=Parallel + run: yarn test:local --filter=testUseEoaSessionSigsToExecuteJsSigning,testUseEoaAuthContextToPkpSign,testUseEoaSessionSigsToPkpSign,testUsePkpSessionSigsToExecuteJsSigning,testUsePkpSessionSigsToPkpSign,testUseValidLitActionCodeGeneratedSessionSigsToPkpSign,testUseValidLitActionCodeGeneratedSessionSigsToExecuteJsSigning,testDelegatingCapacityCreditsNFTToAnotherWalletToExecuteJs,testEthAuthSigToEncryptDecryptString,testExecuteJsSignAndCombineEcdsa,testExecutJsDecryptAndCombine,testExecuteJsBroadcastAndCollect --exclude=Parallel - name: Get Container Logs if: always() run: docker logs shiva diff --git a/local-tests/test.ts b/local-tests/test.ts index f4154aa42..7a55853d9 100644 --- a/local-tests/test.ts +++ b/local-tests/test.ts @@ -2,6 +2,7 @@ import { TinnyEnvironment } from './setup/tinny-environment'; import { runInBand, runTestsParallel } from './setup/tinny-operations'; // import { testBundleSpeed } from './tests/test-bundle-speed'; // import { testExample } from './tests/test-example'; +import { testUseEoaAuthContextToPkpSign } from 'local-tests/tests/testUseEoaAuthContextToPkpSign'; import { testUseEoaSessionSigsToExecuteJsSigning } from './tests/testUseEoaSessionSigsToExecuteJsSigning'; import { testUseEoaSessionSigsToPkpSign } from './tests/testUseEoaSessionSigsToPkpSign'; import { testUsePkpSessionSigsToExecuteJsSigning } from './tests/testUsePkpSessionSigsToExecuteJsSigning'; @@ -163,6 +164,7 @@ setLitActionsCodeToLocal(); }; const eoaSessionSigsTests = { + testUseEoaAuthContextToPkpSign, testUseEoaSessionSigsToExecuteJsSigning, testUseEoaSessionSigsToPkpSign, testUseEoaSessionSigsToExecuteJsSigningInParallel, diff --git a/local-tests/tests/testUseEoaAuthContextToPkpSign.ts b/local-tests/tests/testUseEoaAuthContextToPkpSign.ts new file mode 100644 index 000000000..ef78834c7 --- /dev/null +++ b/local-tests/tests/testUseEoaAuthContextToPkpSign.ts @@ -0,0 +1,127 @@ +import { ethers } from 'ethers'; + +import { LitPKPResource } from '@lit-protocol/auth-helpers'; +import { LIT_ABILITY } from '@lit-protocol/constants'; +import { EthWalletProvider } from '@lit-protocol/lit-auth-client'; +import { LitNodeClient } from '@lit-protocol/lit-node-client'; +import { log } from '@lit-protocol/misc'; +import { AuthCallbackParams, AuthSig } from '@lit-protocol/types'; +import { TinnyEnvironment } from 'local-tests/setup/tinny-environment'; + +/** + * Test Commands: + * ✅ NETWORK=datil-dev yarn test:local --filter=testUseEoaAuthContextToPkpSign + * ✅ NETWORK=datil-test yarn test:local --filter=testUseEoaAuthContextToPkpSign + * ✅ NETWORK=custom yarn test:local --filter=testUseEoaAuthContextToPkpSign + */ +export const testUseEoaAuthContextToPkpSign = async ( + devEnv: TinnyEnvironment +) => { + const alice = await devEnv.createRandomPerson(); + + const ONE_MINUTE = 1 * 60 * 1000; + const expiration = new Date(Date.now() + ONE_MINUTE).toISOString(); + const resourceAbilityRequests = [ + { + resource: new LitPKPResource('*'), + ability: LIT_ABILITY.PKPSigning, + }, + ]; + const litNodeClient = new LitNodeClient({ + litNetwork: alice.envConfig.network, + authContext: { + getSessionSigsProps: { + resourceAbilityRequests, + expiration, + authNeededCallback: async function ( + params: AuthCallbackParams + ): Promise { + const response = await litNodeClient.signSessionKey({ + sessionKey: params.sessionKey, + statement: params.statement || 'Some custom statement.', + authMethods: [ + await EthWalletProvider.authenticate({ + signer: alice.wallet, + litNodeClient: litNodeClient, + expiration: params.expiration, + }), + ], + pkpPublicKey: alice.pkp.publicKey, + expiration: params.expiration, + resources: params.resources, + chainId: 1, + + // -- required fields + resourceAbilityRequests: params.resourceAbilityRequests, + }); + + return response.authSig; + }, + }, + }, + }); + + const runWithAuthContext = await litNodeClient.pkpSign({ + toSign: alice.loveLetter, + pubKey: alice.pkp.publicKey, + }); + + devEnv.releasePrivateKeyFromUser(alice); + + // Expected output: + // { + // r: "25fc0d2fecde8ed801e9fee5ad26f2cf61d82e6f45c8ad1ad1e4798d3b747fd9", + // s: "549fe745b4a09536e6e7108d814cf7e44b93f1d73c41931b8d57d1b101833214", + // recid: 1, + // signature: "0x25fc0d2fecde8ed801e9fee5ad26f2cf61d82e6f45c8ad1ad1e4798d3b747fd9549fe745b4a09536e6e7108d814cf7e44b93f1d73c41931b8d57d1b1018332141c", + // publicKey: "04A3CD53CCF63597D3FFCD1DF1E8236F642C7DF8196F532C8104625635DC55A1EE59ABD2959077432FF635DF2CED36CC153050902B71291C4D4867E7DAAF964049", + // dataSigned: "7D87C5EA75F7378BB701E404C50639161AF3EFF66293E9F375B5F17EB50476F4", + // } + + // -- assertions + // r, s, dataSigned, and public key should be present + if (!runWithAuthContext.r) { + throw new Error(`Expected "r" in runWithAuthContext`); + } + if (!runWithAuthContext.s) { + throw new Error(`Expected "s" in runWithAuthContext`); + } + if (!runWithAuthContext.dataSigned) { + throw new Error(`Expected "dataSigned" in runWithAuthContext`); + } + if (!runWithAuthContext.publicKey) { + throw new Error(`Expected "publicKey" in runWithAuthContext`); + } + + // signature must start with 0x + if (!runWithAuthContext.signature.startsWith('0x')) { + throw new Error(`Expected "signature" to start with 0x`); + } + + // recid must be parseable as a number + if (isNaN(runWithAuthContext.recid)) { + throw new Error(`Expected "recid" to be parseable as a number`); + } + + const signature = ethers.utils.joinSignature({ + r: '0x' + runWithAuthContext.r, + s: '0x' + runWithAuthContext.s, + recoveryParam: runWithAuthContext.recid, + }); + const recoveredPubKey = ethers.utils.recoverPublicKey( + alice.loveLetter, + signature + ); + if (recoveredPubKey !== `0x${runWithAuthContext.publicKey.toLowerCase()}`) { + throw new Error( + `Expected recovered public key to match runWithAuthContext.publicKey` + ); + } + if (recoveredPubKey !== `0x${alice.pkp.publicKey.toLowerCase()}`) { + throw new Error( + `Expected recovered public key to match alice.pkp.publicKey` + ); + } + + log('✅ testUseEoaAuthContextToPkpSign'); +}; From dd42b70c44589c93e8cfefd66ef852a343e3d275 Mon Sep 17 00:00:00 2001 From: FedericoAmura Date: Mon, 30 Dec 2024 20:20:52 +0100 Subject: [PATCH 8/9] feat: add assertConnected at pkpSign --- .../tests/testUseEoaAuthContextToPkpSign.ts | 55 ++++++++----------- .../src/lib/lit-node-client-nodejs.ts | 2 + 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/local-tests/tests/testUseEoaAuthContextToPkpSign.ts b/local-tests/tests/testUseEoaAuthContextToPkpSign.ts index ef78834c7..d25ab3d82 100644 --- a/local-tests/tests/testUseEoaAuthContextToPkpSign.ts +++ b/local-tests/tests/testUseEoaAuthContextToPkpSign.ts @@ -1,9 +1,7 @@ import { ethers } from 'ethers'; -import { LitPKPResource } from '@lit-protocol/auth-helpers'; +import { createSiweMessageWithRecaps, generateAuthSig, LitPKPResource } from '@lit-protocol/auth-helpers'; import { LIT_ABILITY } from '@lit-protocol/constants'; -import { EthWalletProvider } from '@lit-protocol/lit-auth-client'; -import { LitNodeClient } from '@lit-protocol/lit-node-client'; import { log } from '@lit-protocol/misc'; import { AuthCallbackParams, AuthSig } from '@lit-protocol/types'; import { TinnyEnvironment } from 'local-tests/setup/tinny-environment'; @@ -27,36 +25,31 @@ export const testUseEoaAuthContextToPkpSign = async ( ability: LIT_ABILITY.PKPSigning, }, ]; - const litNodeClient = new LitNodeClient({ - litNetwork: alice.envConfig.network, - authContext: { - getSessionSigsProps: { - resourceAbilityRequests, - expiration, - authNeededCallback: async function ( - params: AuthCallbackParams - ): Promise { - const response = await litNodeClient.signSessionKey({ - sessionKey: params.sessionKey, - statement: params.statement || 'Some custom statement.', - authMethods: [ - await EthWalletProvider.authenticate({ - signer: alice.wallet, - litNodeClient: litNodeClient, - expiration: params.expiration, - }), - ], - pkpPublicKey: alice.pkp.publicKey, - expiration: params.expiration, - resources: params.resources, - chainId: 1, + const litNodeClient = alice.envConfig.litNodeClient; - // -- required fields - resourceAbilityRequests: params.resourceAbilityRequests, - }); + litNodeClient.setAuthContext({ + getSessionSigsProps: { + chain: 'ethereum', + resourceAbilityRequests, + expiration, + authNeededCallback: async function ( + params: AuthCallbackParams + ): Promise { + const toSign = await createSiweMessageWithRecaps({ + uri: params.uri, + expiration: params.expiration, + resources: params.resourceAbilityRequests, + walletAddress: alice.wallet.address, + nonce: await litNodeClient.getLatestBlockhash(), + litNodeClient: devEnv.litNodeClient, + }); - return response.authSig; - }, + const authSig = await generateAuthSig({ + signer: alice.wallet, + toSign, + }); + + return authSig; }, }, }); 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 f1abbcdb4..ec97da299 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 @@ -1130,6 +1130,8 @@ export class LitNodeClientNodeJs * @param [params.getSessionSigsPropsOverride] - The props override when obtaining sessionSigs from the auth context */ pkpSign = async (params: JsonPkpSignSdkParams): Promise => { + await this.assertConnected(); + // -- validate required params const requiredParamKeys = ['toSign', 'pubKey']; (requiredParamKeys as (keyof JsonPkpSignSdkParams)[]).forEach((key) => { From ec0861dc9763ba11d62b0e063a3f55f3b1b4397b Mon Sep 17 00:00:00 2001 From: FedericoAmura Date: Mon, 30 Dec 2024 20:41:39 +0100 Subject: [PATCH 9/9] fix: add lit action resource to auth context to pass test --- local-tests/tests/testUseEoaAuthContextToPkpSign.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/local-tests/tests/testUseEoaAuthContextToPkpSign.ts b/local-tests/tests/testUseEoaAuthContextToPkpSign.ts index d25ab3d82..f2e16f47c 100644 --- a/local-tests/tests/testUseEoaAuthContextToPkpSign.ts +++ b/local-tests/tests/testUseEoaAuthContextToPkpSign.ts @@ -1,6 +1,11 @@ import { ethers } from 'ethers'; -import { createSiweMessageWithRecaps, generateAuthSig, LitPKPResource } from '@lit-protocol/auth-helpers'; +import { + createSiweMessageWithRecaps, + generateAuthSig, + LitActionResource, + LitPKPResource, +} from '@lit-protocol/auth-helpers'; import { LIT_ABILITY } from '@lit-protocol/constants'; import { log } from '@lit-protocol/misc'; import { AuthCallbackParams, AuthSig } from '@lit-protocol/types'; @@ -24,6 +29,10 @@ export const testUseEoaAuthContextToPkpSign = async ( resource: new LitPKPResource('*'), ability: LIT_ABILITY.PKPSigning, }, + { + resource: new LitActionResource('*'), + ability: LIT_ABILITY.LitActionExecution, + }, ]; const litNodeClient = alice.envConfig.litNodeClient;