diff --git a/package.json b/package.json index 30a9c3d36..11b056915 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@changesets/cli": "^2.28.1", - "@nucypher/nucypher-core": "^0.14.5", + "@nucypher/nucypher-core": "^0.15.1-dev.1", "ethers": "^5.8.0" }, "devDependencies": { @@ -65,7 +65,7 @@ ] }, "overrides": { - "@nucypher/nucypher-core": "^0.14.5", + "@nucypher/nucypher-core": "^0.15.1-dev.1", "glob-parent@<5.1.2": ">=5.1.2", "node-forge@<1.0.0": ">=1.0.0", "node-forge@<1.3.0": ">=1.3.0", diff --git a/packages/pre/package.json b/packages/pre/package.json index 6aa3ccf7f..71437f31e 100644 --- a/packages/pre/package.json +++ b/packages/pre/package.json @@ -38,7 +38,7 @@ "typedoc": "typedoc" }, "dependencies": { - "@nucypher/nucypher-core": "^0.14.5", + "@nucypher/nucypher-core": "^0.15.1-dev.1", "@nucypher/shared": "workspace:*", "ethers": "^5.8.0" }, diff --git a/packages/shared/package.json b/packages/shared/package.json index 22464a787..59f1c73a0 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -43,7 +43,7 @@ "@ethersproject/abi": "^5.8.0", "@ethersproject/providers": "^5.8.0", "@nucypher/nucypher-contracts": "0.26.0-alpha.1", - "@nucypher/nucypher-core": "^0.14.5", + "@nucypher/nucypher-core": "^0.15.1-dev.1", "axios": "^1.8.4", "deep-equal": "^2.2.3", "ethers": "^5.8.0", diff --git a/packages/shared/src/contracts/agents/signing-coordinator.ts b/packages/shared/src/contracts/agents/signing-coordinator.ts index 251647576..c2fe8b507 100644 --- a/packages/shared/src/contracts/agents/signing-coordinator.ts +++ b/packages/shared/src/contracts/agents/signing-coordinator.ts @@ -5,7 +5,7 @@ import { Domain } from '../../porter'; import { SigningCoordinator__factory } from '../ethers-typechain'; import { SigningCoordinator } from '../ethers-typechain/SigningCoordinator'; -type SignerInfo = { +export type SignerInfo = { operator: string; provider: string; signature: string; diff --git a/packages/shared/src/porter.ts b/packages/shared/src/porter.ts index e658c19a2..c3b782f39 100644 --- a/packages/shared/src/porter.ts +++ b/packages/shared/src/porter.ts @@ -2,9 +2,12 @@ import { CapsuleFrag, EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, + PackedUserOperationSignatureRequest, PublicKey, RetrievalKit, + SignatureResponse, TreasureMap, + UserOperationSignatureRequest, } from '@nucypher/nucypher-core'; import axios, { AxiosRequestConfig, @@ -148,18 +151,24 @@ type PostTacoDecryptResponse = { }; export type TacoDecryptResult = { - encryptedResponses: Record; - errors: Record; + encryptedResponses: Record< + ChecksumAddress, + EncryptedThresholdDecryptionResponse + >; + errors: Record; }; // Signing types + +type PostTacoSignRequest = { + readonly signing_requests: Record; + readonly threshold: number; +}; + type TacoSignResponse = { readonly result: { readonly signing_results: { - readonly signatures: Record< - ChecksumAddress, - [ChecksumAddress, Base64EncodedBytes] - >; + readonly signatures: Record; readonly errors: Record; }; }; @@ -172,32 +181,10 @@ export type TacoSignature = { }; export type TacoSignResult = { - signingResults: { [ursulaAddress: string]: TacoSignature }; - errors: Record; + signingResults: Record; + errors: Record; }; -function decodeSignature( - signerAddress: string, - signatureB64: string, -): { result?: TacoSignature; error?: string } { - try { - const decodedData = JSON.parse( - new TextDecoder().decode(fromBase64(signatureB64)), - ); - return { - result: { - messageHash: decodedData.message_hash, - signature: decodedData.signature, - signerAddress, - }, - }; - } catch (error) { - return { - error: `Failed to decode signature: ${error}`, - }; - } -} - export class PorterClient { readonly porterUrls: URL[]; @@ -345,12 +332,20 @@ export class PorterClient { } public async signUserOp( - signingRequests: Record, + signingRequests: Record< + string, + UserOperationSignatureRequest | PackedUserOperationSignatureRequest + >, threshold: number, ): Promise { - const data: Record = { - signing_requests: signingRequests, - threshold: threshold, + const data: PostTacoSignRequest = { + signing_requests: Object.fromEntries( + Object.entries(signingRequests).map(([ursula, signingRequest]) => [ + ursula, + toBase64(signingRequest.toBytes()), + ]), + ), + threshold, }; const resp: AxiosResponse = await this.tryAndCall({ @@ -358,27 +353,25 @@ export class PorterClient { method: 'post', data, }); - const { signatures, errors } = resp.data.result.signing_results; - const allErrors: Record = { ...errors }; const signingResults: { [ursulaAddress: string]: TacoSignature } = {}; - for (const [ursulaAddress, [signerAddress, signatureB64]] of Object.entries( + for (const [ursulaAddress, signatureResponseBase64] of Object.entries( signatures || {}, )) { - const decoded = decodeSignature(signerAddress, signatureB64); - if (decoded.error) { - // issue with decoding signature, add to errors - allErrors[ursulaAddress] = decoded.error; - continue; - } - // Always include all decoded signatures in signingResults - signingResults[ursulaAddress] = decoded.result!; + const signatureResponse = SignatureResponse.fromBytes( + fromBase64(signatureResponseBase64 as string), + ); + signingResults[ursulaAddress] = { + messageHash: `0x${toHexString(signatureResponse.hash)}`, + signature: `0x${toHexString(signatureResponse.signature)}`, + signerAddress: signatureResponse.signer, + }; } return { signingResults, - errors: allErrors, + errors, }; } } diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts index 9148cef91..8d4b96cdb 100644 --- a/packages/shared/src/types.ts +++ b/packages/shared/src/types.ts @@ -1,82 +1,117 @@ -import { ethers } from 'ethers'; +import { PackedUserOperation, UserOperation } from '@nucypher/nucypher-core'; + +import { fromHexString } from './utils'; export type ChecksumAddress = `0x${string}`; export type HexEncodedBytes = string; export type Base64EncodedBytes = string; -export type UserOperation = { - sender: string; - nonce: number; - factory: string; - factoryData: string; - callData: string; - callGasLimit: number; - verificationGasLimit: number; - preVerificationGas: number; - maxFeePerGas: number; - maxPriorityFeePerGas: number; - paymaster: string; - paymasterVerificationGasLimit: number; - paymasterPostOpGasLimit: number; - paymasterData: string; - signature: string; +export type UserOperationToSign = { + sender: `0x${string}`; + nonce: bigint | number; + callData: `0x${string}` | Uint8Array; + callGasLimit: bigint | number; + verificationGasLimit: bigint | number; + preVerificationGas: bigint | number; + maxFeePerGas: bigint | number; + maxPriorityFeePerGas: bigint | number; + // optional fields + factory?: `0x${string}` | undefined; + factoryData?: `0x${string}` | Uint8Array | undefined; + paymaster?: `0x${string}` | undefined; + paymasterVerificationGasLimit?: bigint | number | undefined; + paymasterPostOpGasLimit?: bigint | number | undefined; + paymasterData?: `0x${string}` | Uint8Array | undefined; + signature?: `0x${string}` | Uint8Array | undefined; }; -export class UserOperationSignatureRequest { - constructor( - private userOp: ReturnType, - private aaVersion: string, - private cohortId: number, - private chainId: number, - private context: unknown = {}, - private signatureType: string = 'userop', - ) {} +export type PackedUserOperationToSign = { + sender: `0x${string}`; + nonce: bigint | number; + initCode: `0x${string}` | Uint8Array; + callData: `0x${string}` | Uint8Array; + accountGasLimit: `0x${string}` | Uint8Array; + preVerificationGas: bigint | number; + gasFees: `0x${string}` | Uint8Array; + paymasterAndData: `0x${string}` | Uint8Array; +}; - toBytes(): Uint8Array { - const data = { - user_op: JSON.stringify(this.userOp), - aa_version: this.aaVersion, - cohort_id: this.cohortId, - chain_id: this.chainId, - context: this.context, - signature_type: this.signatureType, - }; - return new TextEncoder().encode(JSON.stringify(data)); - } +function getBigIntValue(value: bigint | number): bigint { + return typeof value === 'bigint' ? value : BigInt(value); } -function normalizeAddress(address: string): string | null { - if (!address || address === '0x') { - return null; +function getUint8ArrayValue(value: `0x${string}` | Uint8Array): Uint8Array { + return value instanceof Uint8Array ? value : fromHexString(value); +} + +export function toCoreUserOperation( + userOperation: UserOperationToSign, +): UserOperation { + const userOp = new UserOperation( + userOperation.sender, + getBigIntValue(userOperation.nonce), + getUint8ArrayValue(userOperation.callData), + getBigIntValue(userOperation.callGasLimit), + getBigIntValue(userOperation.verificationGasLimit), + getBigIntValue(userOperation.preVerificationGas), + getBigIntValue(userOperation.maxFeePerGas), + getBigIntValue(userOperation.maxPriorityFeePerGas), + ); + + // optional factory data + if (userOperation.factory) { + let factory_data = undefined; + if (userOperation.factoryData) { + factory_data = getUint8ArrayValue(userOperation.factoryData); + } + + userOp.setFactoryData(userOperation.factory, factory_data); } - try { - // Use ethers to get the checksummed address - this will throw on invalid addresses - return ethers.utils.getAddress(address); - } catch (error) { - // Re-throw the error to fail fast on invalid addresses - throw new Error( - `Invalid address: ${address}. ${error instanceof Error ? error.message : 'Unknown error'}`, + // optional paymaster data + if (userOperation.paymaster) { + if (!userOperation.paymasterVerificationGasLimit) { + throw new Error( + 'paymasterVerificationGasLimit is required when paymaster is set', + ); + } else if (!userOperation.paymasterPostOpGasLimit) { + throw new Error( + 'paymasterPostOpGasLimit is required when paymaster is set', + ); + } + + userOp.setPaymasterData( + userOperation.paymaster, + getBigIntValue(userOperation.paymasterVerificationGasLimit), + getBigIntValue(userOperation.paymasterPostOpGasLimit), + userOperation.paymasterData + ? getUint8ArrayValue(userOperation.paymasterData) + : undefined, ); } + + return userOp; +} + +export function toCorePackedUserOperation( + packedUserOperation: PackedUserOperationToSign, +): PackedUserOperation { + const packedUserOp = new PackedUserOperation( + packedUserOperation.sender, + getBigIntValue(packedUserOperation.nonce), + getUint8ArrayValue(packedUserOperation.initCode), + getUint8ArrayValue(packedUserOperation.callData), + getUint8ArrayValue(packedUserOperation.accountGasLimit), + getBigIntValue(packedUserOperation.preVerificationGas), + getUint8ArrayValue(packedUserOperation.gasFees), + getUint8ArrayValue(packedUserOperation.paymasterAndData), + ); + + return packedUserOp; } -export function convertUserOperationToPython(userOp: UserOperation) { - return { - sender: normalizeAddress(userOp.sender), - nonce: userOp.nonce, - factory: normalizeAddress(userOp.factory), - factory_data: userOp.factoryData || '0x', - call_data: userOp.callData || '0x', - call_gas_limit: userOp.callGasLimit, - verification_gas_limit: userOp.verificationGasLimit, - pre_verification_gas: userOp.preVerificationGas, - max_fee_per_gas: userOp.maxFeePerGas, - max_priority_fee_per_gas: userOp.maxPriorityFeePerGas, - paymaster: normalizeAddress(userOp.paymaster), - paymaster_verification_gas_limit: userOp.paymasterVerificationGasLimit, - paymaster_post_op_gas_limit: userOp.paymasterPostOpGasLimit, - paymaster_data: userOp.paymasterData || '0x', - signature: userOp.signature || '0x', - }; +export function isPackedUserOperation( + op: UserOperationToSign | PackedUserOperationToSign, +): op is PackedUserOperationToSign { + return 'initCode' in op && 'gasFees' in op; } diff --git a/packages/shared/test/porter.test.ts b/packages/shared/test/porter.test.ts index ccda73b13..c6d84178e 100644 --- a/packages/shared/test/porter.test.ts +++ b/packages/shared/test/porter.test.ts @@ -2,6 +2,14 @@ import { fakeUrsulas } from '@nucypher/test-utils'; import axios, { HttpStatusCode } from 'axios'; import { beforeAll, describe, expect, it, MockInstance, vi } from 'vitest'; +import { + PackedUserOperation, + PackedUserOperationSignatureRequest, + SignatureResponse, + UserOperation, + UserOperationSignatureRequest, +} from '@nucypher/nucypher-core'; + import { domains, getPorterUris, @@ -13,6 +21,7 @@ import { toHexString, Ursula, } from '../src'; +import { fromHexString } from '../src/utils'; const fakePorterUris = [ 'https://_this_should_crash.com/', @@ -57,38 +66,35 @@ const createMockSignResponse = (errorCase?: boolean) => ({ signatures: errorCase ? { '0xabcd': [ - '0xefgh', toBase64( - new TextEncoder().encode( - JSON.stringify({ - message_hash: '0x1234', - signature: '0xbeef', - }), - ), + new SignatureResponse( + '0x0000000000000000000000000000000000000001', + fromHexString('0x1234'), + fromHexString('0xbeef'), + 0, + ).toBytes(), ), ], } : { '0x1234': [ - '0x5678', toBase64( - new TextEncoder().encode( - JSON.stringify({ - message_hash: '0x1234', - signature: '0xdead', - }), - ), + new SignatureResponse( + '0x0000000000000000000000000000000000000002', + fromHexString('0x1234'), + fromHexString('0xdead'), + 0, + ).toBytes(), ), ], '0xabcd': [ - '0xefgh', toBase64( - new TextEncoder().encode( - JSON.stringify({ - message_hash: '0x1234', - signature: '0xbeef', - }), - ), + new SignatureResponse( + '0x0000000000000000000000000000000000000001', + fromHexString('0x1234'), + fromHexString('0xbeef'), + 0, + ).toBytes(), ), ], }, @@ -195,31 +201,80 @@ describe('PorterClient Signing', () => { }); describe('signUserOp', () => { - const mockPackedUserOp = { - sender: '0x1234', - nonce: '0x1', - factory: '0x0', - factoryData: '0x', - callData: '0xabc', - callGasLimit: '0x20000', - verificationGasLimit: '0x15000', - preVerificationGas: '0x1000', - maxFeePerGas: '0xabc', - maxPriorityFeePerGas: '0x123', - paymaster: '0x0', - paymasterVerificationGasLimit: '0x0', - paymasterPostOpGasLimit: '0x0', - paymasterData: '0x', - signature: '0x', - }; + // since this uses wasm it must be called after initialize (beforeAll()) so we use a factory function + const createUserSignatureRequest = () => + new UserOperationSignatureRequest( + new UserOperation( + '0x000000000000000000000000000000000000000a', + BigInt(123), // nonce + fromHexString('0xabc'), // callData + BigInt(456), // callGasLimit + BigInt(789), // verificationGasLimit + BigInt(101112), // preVerificationGasLimit + BigInt(131415), // maxFeePerGas + BigInt(161718), // maxPriorityFeePerGas + ), + 1, // cohort ID + BigInt(1), // chain ID + '0.8.0', + null, + ); it('should successfully sign a UserOperation', async () => { + const userOpSignatureRequest = createUserSignatureRequest(); + + mockSignUserOp(true); + const porterClient = new PorterClient(fakePorterUris[2]); + const result = await porterClient.signUserOp( + { + '0x1234': userOpSignatureRequest, + '0xabcd': userOpSignatureRequest, + }, + 2, + ); + + expect(result).toEqual({ + signingResults: { + '0x1234': { + messageHash: '0x1234', + signature: '0xdead', + signerAddress: '0x0000000000000000000000000000000000000002', + }, + '0xabcd': { + messageHash: '0x1234', + signature: '0xbeef', + signerAddress: '0x0000000000000000000000000000000000000001', + }, + }, + errors: {}, + }); + }); + + it('should successfully sign a PackedUserOperation', async () => { + const packedUserOperationSignatureRequest = + new PackedUserOperationSignatureRequest( + new PackedUserOperation( + '0x000000000000000000000000000000000000000a', + BigInt(123), // nonce + fromHexString('0xabc'), // initCode + fromHexString('0xdef'), // callData + fromHexString('0x01020304'), // accountGasLimit + BigInt(101112), // preVerificationGas + fromHexString('0x05060708'), // gasFees + fromHexString('0x090a0b0c'), // paymasterAndData + ), + 1, // cohort ID + BigInt(1), // chain ID + 'mdt', + null, + ); + mockSignUserOp(true); const porterClient = new PorterClient(fakePorterUris[2]); const result = await porterClient.signUserOp( { - '0x1234': JSON.stringify(mockPackedUserOp), - '0xabcd': JSON.stringify(mockPackedUserOp), + '0x1234': packedUserOperationSignatureRequest, + '0xabcd': packedUserOperationSignatureRequest, }, 2, ); @@ -229,12 +284,12 @@ describe('PorterClient Signing', () => { '0x1234': { messageHash: '0x1234', signature: '0xdead', - signerAddress: '0x5678', + signerAddress: '0x0000000000000000000000000000000000000002', }, '0xabcd': { messageHash: '0x1234', signature: '0xbeef', - signerAddress: '0xefgh', + signerAddress: '0x0000000000000000000000000000000000000001', }, }, errors: {}, @@ -242,14 +297,16 @@ describe('PorterClient Signing', () => { }); it('should handle UserOperation signing failures', async () => { + const userOpSignatureRequest = createUserSignatureRequest(); + mockSignUserOp(false); const porterClient = new PorterClient(fakePorterUris[2]); await expect( porterClient.signUserOp( { - '0x1234': JSON.stringify(mockPackedUserOp), - '0xabcd': JSON.stringify(mockPackedUserOp), + '0x1234': userOpSignatureRequest, + '0xabcd': userOpSignatureRequest, }, 2, ), @@ -257,13 +314,15 @@ describe('PorterClient Signing', () => { }); it('should handle errors from Porter response in UserOperation signing', async () => { + const userOpSignatureRequest = createUserSignatureRequest(); + // Mock a response with errors from Porter mockSignUserOp(true, true); const porterClient = new PorterClient(fakePorterUris[2]); const result = await porterClient.signUserOp( { - '0x1234': JSON.stringify(mockPackedUserOp), - '0xabcd': JSON.stringify(mockPackedUserOp), + '0x1234': userOpSignatureRequest, + '0xabcd': userOpSignatureRequest, }, 2, ); @@ -273,7 +332,7 @@ describe('PorterClient Signing', () => { '0xabcd': { messageHash: '0x1234', signature: '0xbeef', - signerAddress: '0xefgh', + signerAddress: '0x0000000000000000000000000000000000000001', }, }, errors: { @@ -283,32 +342,28 @@ describe('PorterClient Signing', () => { }); it('should include error when message hashes do not match', async () => { + const userOpSignatureRequest = createUserSignatureRequest(); + const createMismatchedResponse = () => ({ result: { signing_results: { signatures: { - '0x1234': [ - '0x5678', - toBase64( - new TextEncoder().encode( - JSON.stringify({ - message_hash: '0x1234', - signature: '0xdead', - }), - ), - ), - ], - '0xabcd': [ - '0xefgh', - toBase64( - new TextEncoder().encode( - JSON.stringify({ - message_hash: '0xdifferent', // Different message hash - signature: '0xbeef', - }), - ), - ), - ], + '0x1234': toBase64( + new SignatureResponse( + '0x0000000000000000000000000000000000000002', + fromHexString('0x1234'), + fromHexString('0xdead'), + 0, + ).toBytes(), + ), + '0xabcd': toBase64( + new SignatureResponse( + '0x0000000000000000000000000000000000000001', + fromHexString('0xdddd'), // Different message hash + fromHexString('0xbeef'), + 0, + ).toBytes(), + ), }, errors: {}, }, @@ -327,8 +382,8 @@ describe('PorterClient Signing', () => { const porterClient = new PorterClient(fakePorterUris[2]); const result = await porterClient.signUserOp( { - '0x1234': JSON.stringify(mockPackedUserOp), - '0xabcd': JSON.stringify(mockPackedUserOp), + '0x1234': userOpSignatureRequest, + '0xabcd': userOpSignatureRequest, }, 2, ); @@ -338,12 +393,12 @@ describe('PorterClient Signing', () => { '0x1234': { messageHash: '0x1234', signature: '0xdead', - signerAddress: '0x5678', + signerAddress: '0x0000000000000000000000000000000000000002', }, '0xabcd': { - messageHash: '0xdifferent', // Different hash + messageHash: '0xdddd', // Different hash signature: '0xbeef', - signerAddress: '0xefgh', + signerAddress: '0x0000000000000000000000000000000000000001', }, }, errors: {}, // No errors - mismatched hashes don't generate errors, just prevent aggregation @@ -351,21 +406,20 @@ describe('PorterClient Signing', () => { }); it('should not return aggregated signature when threshold not met', async () => { + const userOpSignatureRequest = createUserSignatureRequest(); + const createInsufficientResponse = () => ({ result: { signing_results: { signatures: { - '0x1234': [ - '0x5678', - toBase64( - new TextEncoder().encode( - JSON.stringify({ - message_hash: '0x1234', - signature: '0xdead', - }), - ), - ), - ], + '0x1234': toBase64( + new SignatureResponse( + '0x0000000000000000000000000000000000000002', + fromHexString('0x1234'), + fromHexString('0xdead'), + 0, + ).toBytes(), + ), // Only 1 signature, but threshold is 2 }, errors: {}, @@ -385,8 +439,8 @@ describe('PorterClient Signing', () => { const porterClient = new PorterClient(fakePorterUris[2]); const result = await porterClient.signUserOp( { - '0x1234': JSON.stringify(mockPackedUserOp), - '0xabcd': JSON.stringify(mockPackedUserOp), + '0x1234': userOpSignatureRequest, + '0xabcd': userOpSignatureRequest, }, 2, // threshold of 2, but only 1 signature ); @@ -396,7 +450,7 @@ describe('PorterClient Signing', () => { '0x1234': { messageHash: '0x1234', signature: '0xdead', - signerAddress: '0x5678', + signerAddress: '0x0000000000000000000000000000000000000002', }, }, errors: {}, @@ -404,12 +458,14 @@ describe('PorterClient Signing', () => { }); it('should successfully sign', async () => { + const userOpSignatureRequest = createUserSignatureRequest(); + mockSignUserOp(true); const porterClient = new PorterClient(fakePorterUris[2]); const result = await porterClient.signUserOp( { - '0x1234': JSON.stringify(mockPackedUserOp), - '0xabcd': JSON.stringify(mockPackedUserOp), + '0x1234': userOpSignatureRequest, + '0xabcd': userOpSignatureRequest, }, 2, ); @@ -419,111 +475,49 @@ describe('PorterClient Signing', () => { '0x1234': { messageHash: '0x1234', signature: '0xdead', - signerAddress: '0x5678', + signerAddress: '0x0000000000000000000000000000000000000002', }, '0xabcd': { messageHash: '0x1234', signature: '0xbeef', - signerAddress: '0xefgh', + signerAddress: '0x0000000000000000000000000000000000000001', }, }, errors: {}, }); }); - it('should handle decode errors', async () => { - const createOptimisticErrorResponse = () => ({ - result: { - signing_results: { - signatures: { - '0x1234': [ - '0x5678', - toBase64( - new TextEncoder().encode( - JSON.stringify({ - message_hash: '0x1234', - signature: '0xdead', - }), - ), - ), - ], - '0xabcd': ['0xefgh', 'invalid-base64-data!!!'], // Invalid signature data - }, - errors: {}, - }, - }, - }); - - vi.spyOn(axios, 'request').mockImplementation(async (config) => { - if (config.url === '/sign' && config.baseURL === fakePorterUris[2]) { - return Promise.resolve({ - status: HttpStatusCode.Ok, - data: createOptimisticErrorResponse(), - }); - } - }); - - const porterClient = new PorterClient(fakePorterUris[2]); - const result = await porterClient.signUserOp( - { - '0x1234': JSON.stringify(mockPackedUserOp), - '0xabcd': JSON.stringify(mockPackedUserOp), - }, - 2, - ); - - expect(result).toEqual({ - signingResults: { - '0x1234': { - messageHash: '0x1234', - signature: '0xdead', - signerAddress: '0x5678', - }, - }, - errors: { - '0xabcd': expect.stringContaining('Failed to decode signature'), // Decode error - }, - }); - }); - it('should aggregate only threshold-meeting hash', async () => { + const userOpSignatureRequest = createUserSignatureRequest(); + const createMixedHashResponse = () => ({ result: { signing_results: { signatures: { - '0x1234': [ - '0x5678', - toBase64( - new TextEncoder().encode( - JSON.stringify({ - message_hash: '0xhash1', - signature: '0xdead', - }), - ), - ), - ], - '0xabcd': [ - '0xefgh', - toBase64( - new TextEncoder().encode( - JSON.stringify({ - message_hash: '0xhash1', // Same hash, meets threshold - signature: '0xbeef', - }), - ), - ), - ], - '0xdef0': [ - '0xabc1', - toBase64( - new TextEncoder().encode( - JSON.stringify({ - message_hash: '0xhash2', // Different hash, doesn't meet threshold - signature: '0xcafe', - }), - ), - ), - ], + '0x1234': toBase64( + new SignatureResponse( + '0x0000000000000000000000000000000000000002', + fromHexString('0x0001'), + fromHexString('0xdead'), + 0, + ).toBytes(), + ), + '0xabcd': toBase64( + new SignatureResponse( + '0x0000000000000000000000000000000000000001', + fromHexString('0x0001'), // Same hash, meets threshold + fromHexString('0xbeef'), + 0, + ).toBytes(), + ), + '0xdef0': toBase64( + new SignatureResponse( + '0x0000000000000000000000000000000000000003', + fromHexString('0x0002'), // Different hash, doesn't meet threshold + fromHexString('0xcafe'), + 0, + ).toBytes(), + ), }, errors: {}, }, @@ -542,9 +536,9 @@ describe('PorterClient Signing', () => { const porterClient = new PorterClient(fakePorterUris[2]); const result = await porterClient.signUserOp( { - '0x1234': JSON.stringify(mockPackedUserOp), - '0xabcd': JSON.stringify(mockPackedUserOp), - '0xdef0': JSON.stringify(mockPackedUserOp), + '0x1234': userOpSignatureRequest, + '0xabcd': userOpSignatureRequest, + '0xdef0': userOpSignatureRequest, }, 2, // threshold of 2 ); @@ -553,19 +547,19 @@ describe('PorterClient Signing', () => { // different hashes returned separately signingResults: { '0x1234': { - messageHash: '0xhash1', + messageHash: '0x0001', signature: '0xdead', - signerAddress: '0x5678', + signerAddress: '0x0000000000000000000000000000000000000002', }, '0xabcd': { - messageHash: '0xhash1', + messageHash: '0x0001', signature: '0xbeef', - signerAddress: '0xefgh', + signerAddress: '0x0000000000000000000000000000000000000001', }, '0xdef0': { - messageHash: '0xhash2', + messageHash: '0x0002', signature: '0xcafe', - signerAddress: '0xabc1', + signerAddress: '0x0000000000000000000000000000000000000003', }, }, errors: {}, diff --git a/packages/taco/integration-test/sign.test.ts b/packages/taco/integration-test/sign.test.ts index 8a70e4c2d..9f65c92e2 100644 --- a/packages/taco/integration-test/sign.test.ts +++ b/packages/taco/integration-test/sign.test.ts @@ -3,7 +3,7 @@ import { fromHexString, getPorterUris, SigningCoordinatorAgent, - UserOperation, + UserOperationToSign, } from '@nucypher/shared'; import { ethers } from 'ethers'; import { beforeAll, describe, expect, test } from 'vitest'; @@ -43,7 +43,7 @@ describe.skipIf(!process.env.RUNNING_IN_CI)( test('should sign a user operation', async () => { // Create a sample user operation with all required fields - const userOp: UserOperation = { + const userOp: UserOperationToSign = { sender: DUMMY_ADDRESS, nonce: 0, factory: '0x', diff --git a/packages/taco/package.json b/packages/taco/package.json index 32222200f..44744313b 100644 --- a/packages/taco/package.json +++ b/packages/taco/package.json @@ -42,7 +42,7 @@ }, "dependencies": { "@astronautlabs/jsonpath": "^1.1.2", - "@nucypher/nucypher-core": "^0.14.5", + "@nucypher/nucypher-core": "^0.15.1-dev.1", "@nucypher/shared": "workspace:*", "@nucypher/taco-auth": "workspace:*", "ethers": "^5.8.0", diff --git a/packages/taco/src/sign.ts b/packages/taco/src/sign.ts index b22c03c0b..e60fd94e0 100644 --- a/packages/taco/src/sign.ts +++ b/packages/taco/src/sign.ts @@ -1,16 +1,22 @@ import { - convertUserOperationToPython, + PackedUserOperationSignatureRequest, + UserOperationSignatureRequest, +} from '@nucypher/nucypher-core'; +import { Domain, fromHexString, getPorterUris, + isPackedUserOperation, + PackedUserOperationToSign, PorterClient, + SignerInfo, SigningCoordinatorAgent, TacoSignature, TacoSignResult, - toBase64, + toCorePackedUserOperation, + toCoreUserOperation, toHexString, - UserOperation, - UserOperationSignatureRequest, + UserOperationToSign, } from '@nucypher/shared'; import { ethers } from 'ethers'; @@ -53,6 +59,53 @@ function aggregateSignatures( return `0x${toHexString(new Uint8Array(allBytes))}`; } +async function makeSigningRequests( + signers: SignerInfo[], + cohortId: number, + chainId: number, + userOp: UserOperationToSign | PackedUserOperationToSign, + aaVersion: string, + context?: ConditionContext, +): Promise< + Record< + string, + PackedUserOperationSignatureRequest | UserOperationSignatureRequest + > +> { + const coreContext = context ? await context.toCoreContext() : null; + + let signingRequest: + | PackedUserOperationSignatureRequest + | UserOperationSignatureRequest; + if (isPackedUserOperation(userOp)) { + const corePackedUserOp = toCorePackedUserOperation(userOp); + signingRequest = new PackedUserOperationSignatureRequest( + corePackedUserOp, + cohortId, + BigInt(chainId), + aaVersion, + coreContext, + ); + } else { + const coreUserOp = toCoreUserOperation(userOp); + signingRequest = new UserOperationSignatureRequest( + coreUserOp, + cohortId, + BigInt(chainId), + aaVersion, + coreContext, + ); + } + + const signingRequests: Record< + string, + PackedUserOperationSignatureRequest | UserOperationSignatureRequest + > = Object.fromEntries( + signers.map((signer) => [signer.provider, signingRequest]), + ); + return signingRequests; +} + /** * Signs a UserOperation. * @param provider - The Ethereum provider to use for signing. @@ -71,7 +124,7 @@ export async function signUserOp( domain: Domain, cohortId: number, chainId: number, - userOp: UserOperation, + userOp: UserOperationToSign | PackedUserOperationToSign, aaVersion: 'mdt' | '0.8.0' | string, context?: ConditionContext, porterUris?: string[], @@ -93,22 +146,13 @@ export async function signUserOp( cohortId, ); - const pythonUserOp = convertUserOperationToPython(userOp); - - const signingRequest = new UserOperationSignatureRequest( - pythonUserOp, - aaVersion, + const signingRequests = await makeSigningRequests( + signers, cohortId, chainId, - context || {}, - 'userop', - ); - - const signingRequests: Record = Object.fromEntries( - signers.map((signer) => [ - signer.provider, - toBase64(signingRequest.toBytes()), - ]), + userOp, + aaVersion, + context, ); // Build signing request for the user operation @@ -119,7 +163,7 @@ export async function signUserOp( const hashToSignatures: Map< string, - { [ursulaAddress: string]: TacoSignature } + Record > = new Map(); // Single pass: decode signatures and populate signingResults diff --git a/packages/taco/src/types.ts b/packages/taco/src/types.ts index 8871cc860..d41b148c2 100644 --- a/packages/taco/src/types.ts +++ b/packages/taco/src/types.ts @@ -4,5 +4,5 @@ export { Context as CoreContext, } from '@nucypher/nucypher-core'; -// Re-export UserOperation from shared package to avoid duplication -export type { UserOperation } from '@nucypher/shared'; +// Re-export UserOperationToSign from shared package to avoid duplication +export { UserOperationToSign } from '@nucypher/shared'; diff --git a/packages/taco/test/taco-sign.test.ts b/packages/taco/test/taco-sign.test.ts index 824ef1cad..a3552fa06 100644 --- a/packages/taco/test/taco-sign.test.ts +++ b/packages/taco/test/taco-sign.test.ts @@ -1,11 +1,23 @@ import { - convertUserOperationToPython, + PackedUserOperation, + PackedUserOperationSignatureRequest, + UserOperation, + UserOperationSignatureRequest, +} from '@nucypher/nucypher-core'; +import { + fromHexString, + initialize, + isPackedUserOperation, + PackedUserOperationToSign, PorterClient, SigningCoordinatorAgent, + toCorePackedUserOperation, + toCoreUserOperation, + UserOperationToSign, } from '@nucypher/shared'; import { fakePorterUri } from '@nucypher/test-utils'; import { ethers } from 'ethers'; -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { ContractCondition } from '../src/conditions/base/contract'; import { RpcCondition } from '../src/conditions/base/rpc'; @@ -13,10 +25,132 @@ import { CompoundCondition } from '../src/conditions/compound-condition'; import { ConditionExpression } from '../src/conditions/condition-expr'; import { setSigningCohortConditions, signUserOp } from '../src/sign'; +function getNumberValue(value: bigint | number): bigint { + return typeof value === 'bigint' ? value : BigInt(value); +} + +function checkPackedUserOpEquality( + op1: PackedUserOperationToSign, + op2: PackedUserOperation, +) { + expect(op1.sender).toEqual(op2.sender); + expect(getNumberValue(op1.nonce)).toEqual(op2.nonce); + + const initCode = + op1.initCode instanceof Uint8Array + ? op1.initCode + : fromHexString(op1.initCode); + expect(initCode).toEqual(op2.initCode); + + const callData = + op1.callData instanceof Uint8Array + ? op1.callData + : fromHexString(op1.callData); + expect(callData).toEqual(op2.callData); + + const accountGasLimit = + op1.accountGasLimit instanceof Uint8Array + ? op1.accountGasLimit + : fromHexString(op1.accountGasLimit); + expect(accountGasLimit).toEqual(op2.accountGasLimits); + + expect(getNumberValue(op1.preVerificationGas)).toEqual( + op2.preVerificationGas, + ); + + const gasFees = + op1.gasFees instanceof Uint8Array + ? op1.gasFees + : fromHexString(op1.gasFees); + expect(gasFees).toEqual(op2.gasFees); + + const paymasterAndData = + op1.paymasterAndData instanceof Uint8Array + ? op1.paymasterAndData + : fromHexString(op1.paymasterAndData); + expect(paymasterAndData).toEqual(op2.paymasterAndData); +} + +function checkUserOpEquality(op1: UserOperationToSign, op2: UserOperation) { + expect(op1.sender).toEqual(op2.sender); + + expect(getNumberValue(op1.nonce)).toEqual(op2.nonce); + + const callData = + op1.callData instanceof Uint8Array + ? op1.callData + : fromHexString(op1.callData); + expect(callData).toEqual(op2.callData); + + expect(getNumberValue(op1.callGasLimit)).toEqual(op2.callGasLimit); + expect(getNumberValue(op1.verificationGasLimit)).toEqual( + op2.verificationGasLimit, + ); + expect(getNumberValue(op1.preVerificationGas)).toEqual( + op2.preVerificationGas, + ); + expect(getNumberValue(op1.maxFeePerGas)).toEqual(op2.maxFeePerGas); + expect(getNumberValue(op1.maxPriorityFeePerGas)).toEqual( + op2.maxPriorityFeePerGas, + ); + + if (op1.factory === undefined) { + expect(op2.factory).toBeUndefined(); + } else { + expect(op1.factory).toEqual(op2.factory); + } + + if (op1.factoryData === undefined) { + expect(op2.factoryData).toBeUndefined(); + } else { + const factoryData = + op1.factoryData instanceof Uint8Array + ? op1.factoryData + : fromHexString(op1.factoryData); + expect(factoryData).toEqual(op2.factoryData); + } + + if (op1.paymaster === undefined) { + expect(op2.paymaster).toBeUndefined(); + } else { + expect(op1.paymaster).toEqual(op2.paymaster); + } + + if (op1.paymasterVerificationGasLimit === undefined) { + expect(op2.paymasterVerificationGasLimit).toBeUndefined(); + } else { + expect(getNumberValue(op1.paymasterVerificationGasLimit)).toEqual( + op2.paymasterVerificationGasLimit, + ); + } + + if (op1.paymasterPostOpGasLimit === undefined) { + expect(op2.paymasterPostOpGasLimit).toBeUndefined(); + } else { + expect(getNumberValue(op1.paymasterPostOpGasLimit)).toEqual( + op2.paymasterPostOpGasLimit, + ); + } + + if (op1.paymasterData === undefined) { + expect(op2.paymasterData).toBeUndefined(); + } else { + const paymasterData = + op1.paymasterData instanceof Uint8Array + ? op1.paymasterData + : fromHexString(op1.paymasterData); + expect(paymasterData).toEqual(op2.paymasterData); + } +} + describe('TACo Signing', () => { let porterSignUserOpMock: ReturnType; let mockProvider: ethers.providers.Provider; + beforeAll(async () => { + await initialize(); + }); + beforeEach(() => { porterSignUserOpMock = vi.fn(); mockProvider = {} as ethers.providers.Provider; @@ -31,33 +165,176 @@ describe('TACo Signing', () => { vi.spyOn(SigningCoordinatorAgent, 'getThreshold').mockResolvedValue(2); }); + describe('toCoreUserOperation', () => { + const userOpToSign: UserOperationToSign = { + sender: '0x742D35Cc6634C0532925A3b8D33c9c0E7B66C8E8', + nonce: BigInt(1), + callData: fromHexString('0xabc'), + callGasLimit: BigInt(131072), + verificationGasLimit: BigInt(86016), + preVerificationGas: BigInt(4096), + maxFeePerGas: BigInt(2748), + maxPriorityFeePerGas: BigInt(291), + }; + + it('should convert base fields', () => { + const coreUserOp = toCoreUserOperation(userOpToSign); + checkUserOpEquality(userOpToSign, coreUserOp); + }); + it('should allow factory optional fields', () => { + const updatedUserOp: UserOperationToSign = { + ...userOpToSign, + factory: '0x000000000000000000000000000000000000000A', + factoryData: fromHexString('0xdef'), + }; + const coreUserOp = toCoreUserOperation(updatedUserOp); + checkUserOpEquality(updatedUserOp, coreUserOp); + }); + it('should allow factory and no factory data', () => { + const updatedUserOp: UserOperationToSign = { + ...userOpToSign, + factory: '0x000000000000000000000000000000000000000A', + }; + const coreUserOp = toCoreUserOperation(updatedUserOp); + checkUserOpEquality(updatedUserOp, coreUserOp); + }); + it('should allow paymaster optional fields with paymasterData', () => { + const updatedUserOp: UserOperationToSign = { + ...userOpToSign, + paymaster: '0x000000000000000000000000000000000000000C', + paymasterVerificationGasLimit: BigInt(50000), + paymasterPostOpGasLimit: BigInt(30000), + paymasterData: fromHexString('0xdef'), + }; + const coreUserOp = toCoreUserOperation(updatedUserOp); + checkUserOpEquality(updatedUserOp, coreUserOp); + }); + it('should allow paymaster optional fields with no paymasterData', () => { + const updatedUserOp: UserOperationToSign = { + ...userOpToSign, + paymaster: '0x000000000000000000000000000000000000000C', + paymasterVerificationGasLimit: BigInt(50000), + paymasterPostOpGasLimit: BigInt(30000), + }; + const coreUserOp = toCoreUserOperation(updatedUserOp); + checkUserOpEquality(updatedUserOp, coreUserOp); + }); + it('should raise when paymaster specified but other required fields are not present', () => { + let updatedUserOp: UserOperationToSign = { + ...userOpToSign, + paymaster: '0x000000000000000000000000000000000000000C', + // missing required paymasterVerificationGasLimit and paymasterPostOpGasLimit + }; + expect(() => toCoreUserOperation(updatedUserOp)).toThrow( + 'paymasterVerificationGasLimit is required when paymaster is set', + ); + + updatedUserOp = { + ...updatedUserOp, + paymasterVerificationGasLimit: BigInt(50000), + // missing required paymasterPostOpGasLimit + }; + expect(() => toCoreUserOperation(updatedUserOp)).toThrow( + 'paymasterPostOpGasLimit is required when paymaster is set', + ); + }); + it('should handle alternative types: number and byte fields', () => { + const updatedUserOp: UserOperationToSign = { + ...userOpToSign, + // number instead of bigint, hex instead of byte array + nonce: 1, + callData: '0xabc', + callGasLimit: 131072, + verificationGasLimit: 86016, + preVerificationGas: 4096, + maxFeePerGas: 2748, + maxPriorityFeePerGas: 291, + // include optional fields + factory: '0x000000000000000000000000000000000000000A', + factoryData: '0xabc', + + paymaster: '0x000000000000000000000000000000000000000C', + // number instead of big int + paymasterVerificationGasLimit: 50000, + paymasterPostOpGasLimit: 30000, + paymasterData: '0xdef', + }; + const coreUserOp = toCoreUserOperation(updatedUserOp); + checkUserOpEquality(updatedUserOp, coreUserOp); + }); + }); + describe('toCorePackedUserOperation', () => { + const packedUserOpToSign: PackedUserOperationToSign = { + sender: '0x742D35Cc6634C0532925A3b8D33c9c0E7B66C8E8', + nonce: BigInt(123), + initCode: fromHexString('0xabc'), + callData: fromHexString('0xdef'), + accountGasLimit: fromHexString('0x01020304'), + preVerificationGas: BigInt(101112), + gasFees: fromHexString('0x05060708'), + paymasterAndData: fromHexString('0x090a0b0c'), + }; + + it('should convert base fields', () => { + const corePackedUserOp = toCorePackedUserOperation(packedUserOpToSign); + checkPackedUserOpEquality(packedUserOpToSign, corePackedUserOp); + }); + it('should handle alternative types: number and byte fields', () => { + const updatedPackedUserOp: PackedUserOperationToSign = { + // number instead of bigint, hex instead of byte array + ...packedUserOpToSign, + nonce: 1, + initCode: '0xabc', + callData: '0xdef', + accountGasLimit: '0x01020304', + preVerificationGas: 4096, + gasFees: '0x05060708', + paymasterAndData: '0x090a0b0c', + }; + const corePackedUserOp = toCorePackedUserOperation(updatedPackedUserOp); + checkPackedUserOpEquality(updatedPackedUserOp, corePackedUserOp); + }); + }); + describe('signUserOp', () => { - const userOp = { + const userOp: UserOperationToSign = { + sender: '0x742D35Cc6634C0532925A3b8D33c9c0E7B66C8E8', + nonce: BigInt(1), + callData: fromHexString('0xabc'), + callGasLimit: BigInt(131072), + verificationGasLimit: BigInt(86016), + preVerificationGas: BigInt(4096), + maxFeePerGas: BigInt(2748), + maxPriorityFeePerGas: BigInt(291), + }; + const packedUserOp: PackedUserOperationToSign = { sender: '0x742D35Cc6634C0532925A3b8D33c9c0E7B66C8E8', - nonce: 1, - factory: '0x0000000000000000000000000000000000000000', - factoryData: '0x', - callData: '0xabc', - callGasLimit: 131072, - verificationGasLimit: 86016, - preVerificationGas: 4096, - maxFeePerGas: 2748, - maxPriorityFeePerGas: 291, - paymaster: '0x0000000000000000000000000000000000000000', - paymasterVerificationGasLimit: 0, - paymasterPostOpGasLimit: 0, - paymasterData: '0x', - signature: '0x', + nonce: BigInt(1), + initCode: fromHexString('0xabc'), + callData: fromHexString('0xdef'), + accountGasLimit: fromHexString('0x01020304'), + preVerificationGas: BigInt(101112), + gasFees: fromHexString('0x05060708'), + paymasterAndData: fromHexString('0x090a0b0c'), }; + const chainId = 1; const cohortId = 5; const porterUris = [fakePorterUri]; const aaVersion = '0.8.0'; const threshold = 2; - it.each(['0.8.0', 'mdt'])( - 'should sign a user operation with a valid aa versions', - async (validAAVersion) => { + it.each([ + ['0.8.0', userOp], + ['0.8.0', packedUserOp], + ['mdt', userOp], + ['mdt', packedUserOp], + ])( + 'should sign user operation and packed user operations for valid aa versions', + async ( + validAAVersion: string, + userOp: UserOperationToSign | PackedUserOperationToSign, + ) => { const signingResults = { '0xnode1': { messageHash: '0xhash1', @@ -88,33 +365,39 @@ describe('TACo Signing', () => { porterUris, ); - const pythonUserOp = convertUserOperationToPython(userOp); - - expect(porterSignUserOpMock).toHaveBeenCalledWith( - { - '0xnode1': btoa( - JSON.stringify({ - user_op: JSON.stringify(pythonUserOp), - aa_version: validAAVersion, - cohort_id: cohortId, - chain_id: chainId, - context: {}, - signature_type: 'userop', - }), - ), - '0xnode2': btoa( - JSON.stringify({ - user_op: JSON.stringify(pythonUserOp), - aa_version: validAAVersion, - cohort_id: cohortId, - chain_id: chainId, - context: {}, - signature_type: 'userop', - }), - ), - }, - threshold, - ); + if (isPackedUserOperation(userOp)) { + expect(porterSignUserOpMock).toHaveBeenCalledWith( + { + '0xnode1': expect.any(PackedUserOperationSignatureRequest), + '0xnode2': expect.any(PackedUserOperationSignatureRequest), + }, + threshold, + ); + } else { + expect(porterSignUserOpMock).toHaveBeenCalledWith( + { + '0xnode1': expect.any(UserOperationSignatureRequest), + '0xnode2': expect.any(UserOperationSignatureRequest), + }, + threshold, + ); + } + + const call = porterSignUserOpMock.mock.calls.at(-1)!; + const [op] = call; + + const requests = [op['0xnode1'], op['0xnode2']]; + requests.forEach((element) => { + if (isPackedUserOperation(userOp)) { + checkPackedUserOpEquality(userOp, element.packedUserOp); + } else { + checkUserOpEquality(userOp, element.userOp); + } + expect(element.aaVersion).toEqual(validAAVersion); + expect(element.cohortId).toEqual(cohortId); + expect(element.chainId).toEqual(BigInt(chainId)); + expect(element.context).toBeUndefined(); + }); expect(result).toEqual({ messageHash: '0xhash1', @@ -152,32 +435,24 @@ describe('TACo Signing', () => { `Threshold of signatures not met; TACo signing failed with errors: ${JSON.stringify(errors)}`, ); - const pythonUserOp = convertUserOperationToPython(userOp); expect(porterSignUserOpMock).toHaveBeenCalledWith( { - '0xnode1': btoa( - JSON.stringify({ - user_op: JSON.stringify(pythonUserOp), - aa_version: aaVersion, - cohort_id: cohortId, - chain_id: chainId, - context: {}, - signature_type: 'userop', - }), - ), - '0xnode2': btoa( - JSON.stringify({ - user_op: JSON.stringify(pythonUserOp), - aa_version: aaVersion, - cohort_id: cohortId, - chain_id: chainId, - context: {}, - signature_type: 'userop', - }), - ), + '0xnode1': expect.any(UserOperationSignatureRequest), + '0xnode2': expect.any(UserOperationSignatureRequest), }, threshold, ); + const call = porterSignUserOpMock.mock.calls.at(-1)!; + const [op] = call; + + const requests = [op['0xnode1'], op['0xnode2']]; + requests.forEach((element) => { + checkUserOpEquality(userOp, element.userOp); + expect(element.aaVersion).toEqual(aaVersion); + expect(element.cohortId).toEqual(cohortId); + expect(element.chainId).toEqual(BigInt(chainId)); + expect(element.context).toBeUndefined(); + }); }); it('should handle insufficient signatures in Porter response', async () => { const signingResults = { diff --git a/packages/taco/test/taco.test.ts b/packages/taco/test/taco.test.ts index e035f211f..23ce52803 100644 --- a/packages/taco/test/taco.test.ts +++ b/packages/taco/test/taco.test.ts @@ -63,7 +63,7 @@ describe('taco', () => { const { decryptionShares } = fakeTDecFlow({ ...mockedDkg, message: toBytes(message), - dkgPublicKey: mockedDkg.dkg.publicKey(), + dkgPublicKey: mockedDkg.serverAggregate.publicKey, thresholdMessageKit: messageKit, }); const { participantSecrets, participants } = await mockDkgParticipants( diff --git a/packages/taco/test/test-utils.ts b/packages/taco/test/test-utils.ts index a6dfb8f7f..faae09d01 100644 --- a/packages/taco/test/test-utils.ts +++ b/packages/taco/test/test-utils.ts @@ -133,7 +133,7 @@ export const fakeDkgTDecFlowE2E: ( threshold = 4, ) => { const ritual = fakeDkgFlow(variant, ritualId, sharesNum, threshold); - const dkgPublicKey = ritual.dkg.publicKey(); + const dkgPublicKey = ritual.serverAggregate.publicKey; const provider = fakeProvider(); const thresholdMessageKit = await encryptMessage( message, @@ -159,7 +159,7 @@ export const fakeDkgTDecFlowE2E: ( export const fakeCoordinatorRitual = async (): Promise => { const ritual = await fakeDkgTDecFlowE2E(); - const dkgPkBytes = ritual.dkg.publicKey().toBytes(); + const dkgPkBytes = ritual.serverAggregate.publicKey.toBytes(); return { initiator: ritual.validators[0].address.toString(), dkgSize: ritual.sharesNum, @@ -220,10 +220,11 @@ export const fakeDkgRitual = (ritual: { dkg: Dkg; sharesNum: number; threshold: number; + serverAggregate: AggregatedTranscript; }) => { return new DkgRitual( fakeRitualId, - ritual.dkg.publicKey(), + ritual.serverAggregate.publicKey, ritual.sharesNum, ritual.threshold, DkgRitualState.ACTIVE, diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index cea78cb70..4f9f4de61 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -29,7 +29,7 @@ "lint:fix": "pnpm lint --fix" }, "dependencies": { - "@nucypher/nucypher-core": "^0.14.5", + "@nucypher/nucypher-core": "^0.15.1-dev.1", "@nucypher/shared": "workspace:*", "@nucypher/taco-auth": "workspace:*", "axios": "^1.8.4", diff --git a/packages/test-utils/src/utils.ts b/packages/test-utils/src/utils.ts index dc328f23c..77395dd87 100644 --- a/packages/test-utils/src/utils.ts +++ b/packages/test-utils/src/utils.ts @@ -239,7 +239,7 @@ export const fakeDkgFlow = ( for (let i = 0; i < sharesNum; i++) { const keypair = Keypair.random(); validatorKeypairs.push(keypair); - const validator = new Validator(genEthAddr(i), keypair.publicKey); + const validator = new Validator(genEthAddr(i), keypair.publicKey, i); validators.push(validator); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0450d806e..09e835fd0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,7 @@ settings: excludeLinksFromLockfile: false overrides: - '@nucypher/nucypher-core': ^0.14.5 + '@nucypher/nucypher-core': ^0.15.1-dev.1 glob-parent@<5.1.2: '>=5.1.2' node-forge@<1.0.0: '>=1.0.0' node-forge@<1.3.0: '>=1.3.0' @@ -19,8 +19,8 @@ importers: specifier: ^2.28.1 version: 2.29.5 '@nucypher/nucypher-core': - specifier: ^0.14.5 - version: 0.14.5 + specifier: ^0.15.1-dev.1 + version: 0.15.1-dev.1 ethers: specifier: ^5.8.0 version: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -491,8 +491,8 @@ importers: packages/pre: dependencies: '@nucypher/nucypher-core': - specifier: ^0.14.5 - version: 0.14.5 + specifier: ^0.15.1-dev.1 + version: 0.15.1-dev.1 '@nucypher/shared': specifier: workspace:* version: link:../shared @@ -516,8 +516,8 @@ importers: specifier: 0.26.0-alpha.1 version: 0.26.0-alpha.1 '@nucypher/nucypher-core': - specifier: ^0.14.5 - version: 0.14.5 + specifier: ^0.15.1-dev.1 + version: 0.15.1-dev.1 axios: specifier: ^1.8.4 version: 1.10.0 @@ -565,8 +565,8 @@ importers: specifier: ^1.1.2 version: 1.1.2 '@nucypher/nucypher-core': - specifier: ^0.14.5 - version: 0.14.5 + specifier: ^0.15.1-dev.1 + version: 0.15.1-dev.1 '@nucypher/shared': specifier: workspace:* version: link:../shared @@ -621,11 +621,13 @@ importers: specifier: workspace:* version: link:../test-utils + packages/taco-auth/dist/es: {} + packages/test-utils: dependencies: '@nucypher/nucypher-core': - specifier: ^0.14.5 - version: 0.14.5 + specifier: ^0.15.1-dev.1 + version: 0.15.1-dev.1 '@nucypher/shared': specifier: workspace:* version: link:../shared @@ -2548,8 +2550,8 @@ packages: '@nucypher/nucypher-contracts@0.26.0-alpha.1': resolution: {integrity: sha512-ISS66SyKbDLXFF88Z6k8Tnc/L6Shx6tq22CjPpovbSD2PGS/UzE3uDxvFTKZfgcIJYsi7GV/ho2Brsx4sHYTLg==} - '@nucypher/nucypher-core@0.14.5': - resolution: {integrity: sha512-Q3kBuJL0qtTtnxrM5DEQauQUvDlXmwubm9u1h7gbyLhs+aZNC9WDyjEUbE43+uahlHu4k1hKEMxD1gjV165ChA==} + '@nucypher/nucypher-core@0.15.1-dev.1': + resolution: {integrity: sha512-h4sRBk6j+qC8mfevQHMHTbpVB6k0em8NKAeVAO3xH7eloDA8nzoBiKHe9NMeavklUyBwPVQKbDuO4aWQm/SLKg==} '@nucypher/shared@0.5.0': resolution: {integrity: sha512-Y+0oEgVtoud4BP/pvj2elPm5igrQ8AAUwYYmUiLpXNqQMRF4zFnYaGbl+xhKk45CYG8S9cDKJEN8nopzVADTNw==} @@ -12105,14 +12107,14 @@ snapshots: '@nucypher/nucypher-contracts@0.26.0-alpha.1': {} - '@nucypher/nucypher-core@0.14.5': {} + '@nucypher/nucypher-core@0.15.1-dev.1': {} '@nucypher/shared@0.5.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@ethersproject/abi': 5.8.0 '@ethersproject/providers': 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@nucypher/nucypher-contracts': 0.25.0 - '@nucypher/nucypher-core': 0.14.5 + '@nucypher/nucypher-core': 0.15.1-dev.1 axios: 1.10.0 deep-equal: 2.2.3 ethers: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -12138,7 +12140,7 @@ snapshots: '@nucypher/taco@0.6.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@astronautlabs/jsonpath': 1.1.2 - '@nucypher/nucypher-core': 0.14.5 + '@nucypher/nucypher-core': 0.15.1-dev.1 '@nucypher/shared': 0.5.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@nucypher/taco-auth': 0.3.0(bufferutil@4.0.9)(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) ethers: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -15214,8 +15216,8 @@ snapshots: '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.8.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.0) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.0) eslint-plugin-react: 7.37.5(eslint@8.57.0) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.0) @@ -15270,21 +15272,6 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.0))(eslint@8.57.0): - dependencies: - '@nolyfill/is-core-module': 1.0.39 - debug: 4.4.1 - eslint: 8.57.0 - get-tsconfig: 4.10.1 - is-bun-module: 2.0.0 - stable-hash: 0.0.5 - tinyglobby: 0.2.14 - unrs-resolver: 1.9.2 - optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.0) - transitivePeerDependencies: - - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.0): dependencies: '@nolyfill/is-core-module': 1.0.39 @@ -15296,7 +15283,7 @@ snapshots: tinyglobby: 0.2.14 unrs-resolver: 1.9.2 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.0) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -15321,14 +15308,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.12.1(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.8.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.8.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.0))(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -15414,7 +15400,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.0): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -15425,7 +15411,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -15437,7 +15423,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.8.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.8.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack