From 050bd60628a3e7c2e9bd5890e3b0965490022fe0 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 14 Aug 2023 11:16:22 +0200 Subject: [PATCH 1/9] draft taco api --- src/characters/cbd-recipient.ts | 11 ++- src/dkg.ts | 13 +++ src/sdk/strategy/cbd-strategy.ts | 6 +- src/taco.ts | 61 ++++++++++++++ test/unit/cbd-strategy.test.ts | 2 +- test/unit/conditions/condition-expr.test.ts | 44 +++++++++++ test/unit/taco.test.ts | 88 +++++++++++++++++++++ test/utils.ts | 5 +- 8 files changed, 218 insertions(+), 12 deletions(-) create mode 100644 src/taco.ts create mode 100644 test/unit/taco.test.ts diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index ad973616a..585ad7faa 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -14,7 +14,6 @@ import { ethers } from 'ethers'; import { DkgCoordinatorAgent, DkgParticipant } from '../agents/coordinator'; import { ConditionContext } from '../conditions'; -import { DkgRitual } from '../dkg'; import { PorterClient } from '../porter'; import { fromJSON, objectEquals, toJSON } from '../utils'; @@ -31,22 +30,22 @@ export class ThresholdDecrypter { private readonly threshold: number ) {} - public static create(porterUri: string, dkgRitual: DkgRitual) { + public static create(porterUri: string, ritualId: number, threshold: number) { return new ThresholdDecrypter( new PorterClient(porterUri), - dkgRitual.id, - dkgRitual.dkgParams.threshold + ritualId, + threshold ); } // Retrieve and decrypt ciphertext using provider and condition expression public async retrieveAndDecrypt( - provider: ethers.providers.Provider, + web3Provider: ethers.providers.Provider, thresholdMessageKit: ThresholdMessageKit, signer?: ethers.Signer ): Promise { const decryptionShares = await this.retrieve( - provider, + web3Provider, thresholdMessageKit, signer ); diff --git a/src/dkg.ts b/src/dkg.ts index c1ab06499..8fa9dd6ec 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -135,6 +135,19 @@ export class DkgClient { ); } + public static async getFinalizedRitual( + web3Provider: ethers.providers.Web3Provider, + ritualId: number + ): Promise { + const ritual = await DkgClient.getExistingRitual(web3Provider, ritualId); + if (ritual.state !== DkgRitualState.FINALIZED) { + throw new Error( + `Ritual ${ritualId} is not finalized. State: ${ritual.state}` + ); + } + return ritual; + } + // TODO: Without Validator public key in Coordinator, we cannot verify the // transcript. We need to add it to the Coordinator (nucypher-contracts #77). // public async verifyRitual(ritualId: number): Promise { diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index 56a91c6c3..ed0a37d8f 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -78,7 +78,11 @@ export class DeployedCbdStrategy { ) {} public static create(dkgRitual: DkgRitual, porterUri: string) { - const decrypter = ThresholdDecrypter.create(porterUri, dkgRitual); + const decrypter = ThresholdDecrypter.create( + porterUri, + dkgRitual.id, + dkgRitual.dkgParams.threshold + ); return new DeployedCbdStrategy(decrypter, dkgRitual.dkgPublicKey); } diff --git a/src/taco.ts b/src/taco.ts new file mode 100644 index 000000000..3d4228c24 --- /dev/null +++ b/src/taco.ts @@ -0,0 +1,61 @@ +import { Ciphertext, ferveoEncrypt } from '@nucypher/nucypher-core'; +import { ethers } from 'ethers'; + +import { ThresholdDecrypter } from './characters/cbd-recipient'; +import { ConditionExpression } from './conditions'; +import { DkgClient } from './dkg'; +import { toBytes } from './utils'; + +export interface TacoMessageKit { + ciphertext: Ciphertext; + aad: Uint8Array; + ritualId: number; + threshold: number; +} + +export const encrypt = async ( + web3Provider: ethers.providers.Web3Provider, + message: string, + conditions: ConditionExpression, + ritualId: number +): Promise => { + const dkgRitual = await DkgClient.getFinalizedRitual(web3Provider, ritualId); + const aad = conditions.asAad(); + const ciphertext = ferveoEncrypt( + toBytes(message), + aad, + dkgRitual.dkgPublicKey + ); + return { + ciphertext, + aad, + ritualId, + threshold: dkgRitual.dkgParams.threshold, + }; +}; + +export const decrypt = async ( + web3Provider: ethers.providers.Web3Provider, + messageKit: TacoMessageKit, + porterUri: string +): Promise => { + const decrypter = ThresholdDecrypter.create( + porterUri, + messageKit.ritualId, + messageKit.threshold + ); + const condExpr = ConditionExpression.fromAad(messageKit.aad); + // TODO: Need web3Provider to fetch participants from Coordinator to make decryption requests. + // Should we put them into the message kit instead? + // Consider case where participants are changing over time. Is that an issue we should consider now? + return decrypter.retrieveAndDecrypt( + web3Provider, + condExpr, + messageKit.ciphertext + ); +}; + +export const taco = { + encrypt, + decrypt, +}; diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index 7251262b1..2dfc76fca 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -51,11 +51,11 @@ const makeCbdStrategy = async () => { async function makeDeployedCbdStrategy() { const strategy = await makeCbdStrategy(); - const mockedDkg = fakeDkgFlow(variant, 0, 4, 4); const mockedDkgRitual = fakeDkgRitual(mockedDkg); const getUrsulasSpy = mockGetUrsulas(ursulas); const getExistingRitualSpy = mockGetExistingRitual(mockedDkgRitual); + const deployedStrategy = await strategy.deploy(aliceProvider, ritualId); expect(getUrsulasSpy).toHaveBeenCalled(); diff --git a/test/unit/conditions/condition-expr.test.ts b/test/unit/conditions/condition-expr.test.ts index 1ef17774a..abdf09f5f 100644 --- a/test/unit/conditions/condition-expr.test.ts +++ b/test/unit/conditions/condition-expr.test.ts @@ -221,6 +221,50 @@ describe('condition set', () => { } ); + it.each([ + // no "operator" nor "method" value + { + version: ConditionExpression.VERSION, + condition: { + randoKey: 'randoValue', + otherKey: 'otherValue', + }, + }, + // invalid "method" and no "contractAddress" + { + version: ConditionExpression.VERSION, + condition: { + method: 'doWhatIWant', + returnValueTest: { + index: 0, + comparator: '>', + value: '100', + }, + chain: 5, + }, + }, + // condition with wrong method "method" and no contract address + { + version: ConditionExpression.VERSION, + condition: { + ...testTimeConditionObj, + method: 'doWhatIWant', + }, + }, + // rpc condition (no contract address) with disallowed method + { + version: ConditionExpression.VERSION, + condition: { + ...testRpcConditionObj, + method: 'isPolicyActive', + }, + }, + ])("can't determine condition type", (invalidCondition) => { + expect(() => { + ConditionExpression.fromObj(invalidCondition); + }).toThrow('unrecognized condition data'); + }); + it.each(['_invalid_condition_type_', undefined as unknown as string])( 'rejects an invalid condition type', (invalidConditionType) => { diff --git a/test/unit/taco.test.ts b/test/unit/taco.test.ts new file mode 100644 index 000000000..fe74043dd --- /dev/null +++ b/test/unit/taco.test.ts @@ -0,0 +1,88 @@ +import { + FerveoVariant, + SecretKey, + SessionStaticSecret, +} from '@nucypher/nucypher-core'; + +import { conditions } from '../../src'; +import { taco } from '../../src/taco'; +import { toBytes } from '../../src/utils'; +import { + fakeDkgFlow, + fakeDkgParticipants, + fakeDkgRitual, + fakePorterUri, + fakeTDecFlow, + fakeWeb3Provider, + mockCbdDecrypt, + mockGetExistingRitual, + mockGetParticipants, + mockRandomSessionStaticSecret, +} from '../utils'; + +import { aliceSecretKeyBytes } from './testVariables'; + +const { + predefined: { ERC721Ownership }, + ConditionExpression, +} = conditions; + +// Shared test variables +const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); +const ownsNFT = new ERC721Ownership({ + contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', + parameters: [3591], + chain: 5, +}); +const conditionExpr = new ConditionExpression(ownsNFT); +const variant = FerveoVariant.precomputed; +// const ritualId = 0; +const message = 'this is a secret'; + +describe('taco', () => { + it('encrypts and decrypts', async () => { + const mockedDkg = fakeDkgFlow(variant, 0, 4, 4); + const mockedDkgRitual = fakeDkgRitual(mockedDkg); + const web3Provider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); + const getExistingRitualSpy = mockGetExistingRitual(mockedDkgRitual); + + const tacoMk = await taco.encrypt( + web3Provider, + message, + conditionExpr, + mockedDkg.ritualId + ); + + expect(getExistingRitualSpy).toHaveBeenCalled(); + + const { decryptionShares } = fakeTDecFlow({ + ...mockedDkg, + message: toBytes(message), + aad: tacoMk.aad, + ciphertext: tacoMk.ciphertext, + }); + const { participantSecrets, participants } = fakeDkgParticipants( + mockedDkg.ritualId, + variant + ); + const requesterSessionKey = SessionStaticSecret.random(); + const decryptSpy = mockCbdDecrypt( + mockedDkg.ritualId, + decryptionShares, + participantSecrets, + requesterSessionKey.publicKey() + ); + const getParticipantsSpy = mockGetParticipants(participants); + const sessionKeySpy = mockRandomSessionStaticSecret(requesterSessionKey); + + const decryptedMessage = await taco.decrypt( + web3Provider, + tacoMk, + fakePorterUri + ); + expect(getParticipantsSpy).toHaveBeenCalled(); + expect(sessionKeySpy).toHaveBeenCalled(); + expect(decryptSpy).toHaveBeenCalled(); + expect(decryptedMessage).toEqual(toBytes(message)); + }); +}); diff --git a/test/utils.ts b/test/utils.ts index 11f8b6714..e41b00144 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -324,10 +324,7 @@ export const fakeTDecFlow = async ({ thresholdMessageKit, }: FakeDkgRitualFlow) => { // Having aggregated the transcripts, the validators can now create decryption shares - const decryptionShares: ( - | DecryptionSharePrecomputed - | DecryptionShareSimple - )[] = []; + const decryptionShares: DecryptionShareSimple[] = []; zip(validators, validatorKeypairs).forEach(([validator, keypair]) => { const dkg = new Dkg(ritualId, sharesNum, threshold, validators, validator); const aggregate = dkg.aggregateTranscript(receivedMessages); From 3c6f0de3cb027b8dc56acb9594d796aa0f4602db Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 14 Aug 2023 13:16:02 +0200 Subject: [PATCH 2/9] draft light encryption method --- src/taco.ts | 42 ++++++++++++++++++++++++++++++++---------- test/unit/taco.test.ts | 1 - 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/taco.ts b/src/taco.ts index 3d4228c24..816d24943 100644 --- a/src/taco.ts +++ b/src/taco.ts @@ -1,4 +1,8 @@ -import { Ciphertext, ferveoEncrypt } from '@nucypher/nucypher-core'; +import { + Ciphertext, + DkgPublicKey, + ferveoEncrypt, +} from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; import { ThresholdDecrypter } from './characters/cbd-recipient'; @@ -9,7 +13,10 @@ import { toBytes } from './utils'; export interface TacoMessageKit { ciphertext: Ciphertext; aad: Uint8Array; + // TODO: How do we get rid of these two fields? We need them for decrypting + // We ritualId in order to fetch the DKG participants and create DecryptionRequests for them ritualId: number; + // We need to know the threshold in order to create DecryptionRequests threshold: number; } @@ -20,17 +27,30 @@ export const encrypt = async ( ritualId: number ): Promise => { const dkgRitual = await DkgClient.getFinalizedRitual(web3Provider, ritualId); - const aad = conditions.asAad(); - const ciphertext = ferveoEncrypt( - toBytes(message), - aad, - dkgRitual.dkgPublicKey + return await encryptLight( + message, + conditions, + dkgRitual.dkgPublicKey, + dkgRitual.dkgParams.threshold, + ritualId ); +}; + +export const encryptLight = async ( + message: string, + conditions: ConditionExpression, + dkgPublicKey: DkgPublicKey, + // TODO: Remove these parameters after fixing TacoMessageKit + threshold: number, + ritualId: number +): Promise => { + const aad = conditions.asAad(); + const ciphertext = ferveoEncrypt(toBytes(message), aad, dkgPublicKey); return { ciphertext, aad, + threshold, ritualId, - threshold: dkgRitual.dkgParams.threshold, }; }; @@ -45,9 +65,10 @@ export const decrypt = async ( messageKit.threshold ); const condExpr = ConditionExpression.fromAad(messageKit.aad); - // TODO: Need web3Provider to fetch participants from Coordinator to make decryption requests. - // Should we put them into the message kit instead? - // Consider case where participants are changing over time. Is that an issue we should consider now? + // TODO: We need web3Provider to fetch participants from Coordinator to make decryption requests. + // Removing this dependency is tied to release of ThresholdMessageKit + // Blocked by changes to nucypher-core and nucypher: + // https://github.com/nucypher/nucypher/pull/3194 return decrypter.retrieveAndDecrypt( web3Provider, condExpr, @@ -57,5 +78,6 @@ export const decrypt = async ( export const taco = { encrypt, + encryptLight, decrypt, }; diff --git a/test/unit/taco.test.ts b/test/unit/taco.test.ts index fe74043dd..907b58f86 100644 --- a/test/unit/taco.test.ts +++ b/test/unit/taco.test.ts @@ -36,7 +36,6 @@ const ownsNFT = new ERC721Ownership({ }); const conditionExpr = new ConditionExpression(ownsNFT); const variant = FerveoVariant.precomputed; -// const ritualId = 0; const message = 'this is a secret'; describe('taco', () => { From 6ad6130012c05cf294ec18963ad531a9d8a72e06 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 24 Aug 2023 13:08:49 +0200 Subject: [PATCH 3/9] hide condition expression from the taco api --- src/taco.ts | 10 +++++----- test/unit/taco.test.ts | 4 +--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/taco.ts b/src/taco.ts index 816d24943..f378477a3 100644 --- a/src/taco.ts +++ b/src/taco.ts @@ -6,7 +6,7 @@ import { import { ethers } from 'ethers'; import { ThresholdDecrypter } from './characters/cbd-recipient'; -import { ConditionExpression } from './conditions'; +import { Condition, ConditionExpression } from './conditions'; import { DkgClient } from './dkg'; import { toBytes } from './utils'; @@ -23,13 +23,13 @@ export interface TacoMessageKit { export const encrypt = async ( web3Provider: ethers.providers.Web3Provider, message: string, - conditions: ConditionExpression, + condition: Condition, ritualId: number ): Promise => { const dkgRitual = await DkgClient.getFinalizedRitual(web3Provider, ritualId); return await encryptLight( message, - conditions, + condition, dkgRitual.dkgPublicKey, dkgRitual.dkgParams.threshold, ritualId @@ -38,13 +38,13 @@ export const encrypt = async ( export const encryptLight = async ( message: string, - conditions: ConditionExpression, + condition: Condition, dkgPublicKey: DkgPublicKey, // TODO: Remove these parameters after fixing TacoMessageKit threshold: number, ritualId: number ): Promise => { - const aad = conditions.asAad(); + const aad = new ConditionExpression(condition).asAad(); const ciphertext = ferveoEncrypt(toBytes(message), aad, dkgPublicKey); return { ciphertext, diff --git a/test/unit/taco.test.ts b/test/unit/taco.test.ts index 907b58f86..619620202 100644 --- a/test/unit/taco.test.ts +++ b/test/unit/taco.test.ts @@ -24,7 +24,6 @@ import { aliceSecretKeyBytes } from './testVariables'; const { predefined: { ERC721Ownership }, - ConditionExpression, } = conditions; // Shared test variables @@ -34,7 +33,6 @@ const ownsNFT = new ERC721Ownership({ parameters: [3591], chain: 5, }); -const conditionExpr = new ConditionExpression(ownsNFT); const variant = FerveoVariant.precomputed; const message = 'this is a secret'; @@ -48,7 +46,7 @@ describe('taco', () => { const tacoMk = await taco.encrypt( web3Provider, message, - conditionExpr, + ownsNFT, mockedDkg.ritualId ); From 0319e0fb2b1b9a9d5f47a38e3596fd9e45c7b621 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 24 Aug 2023 13:30:31 +0200 Subject: [PATCH 4/9] set a default porter uri --- src/taco.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/taco.ts b/src/taco.ts index f378477a3..434a587e7 100644 --- a/src/taco.ts +++ b/src/taco.ts @@ -8,6 +8,7 @@ import { ethers } from 'ethers'; import { ThresholdDecrypter } from './characters/cbd-recipient'; import { Condition, ConditionExpression } from './conditions'; import { DkgClient } from './dkg'; +import { getPorterUri } from './porter'; import { toBytes } from './utils'; export interface TacoMessageKit { @@ -57,7 +58,7 @@ export const encryptLight = async ( export const decrypt = async ( web3Provider: ethers.providers.Web3Provider, messageKit: TacoMessageKit, - porterUri: string + porterUri = getPorterUri('tapir') ): Promise => { const decrypter = ThresholdDecrypter.create( porterUri, From 9f974c0f719b1f991144dc7c69c82d03c329aace Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 4 Sep 2023 09:45:04 +0200 Subject: [PATCH 5/9] update after rebase --- src/conditions/context/context.ts | 2 +- src/dkg.ts | 4 +- src/taco.ts | 41 +++++++++---------- test/unit/cbd-strategy.test.ts | 16 ++++---- test/unit/conditions/condition-expr.test.ts | 44 --------------------- test/unit/conditions/context.test.ts | 2 +- test/unit/taco.test.ts | 18 +++++---- test/utils.ts | 14 +++---- 8 files changed, 48 insertions(+), 93 deletions(-) diff --git a/src/conditions/context/context.ts b/src/conditions/context/context.ts index c1d8426df..0b64cad90 100644 --- a/src/conditions/context/context.ts +++ b/src/conditions/context/context.ts @@ -51,7 +51,7 @@ export class ConditionContext { ); if (conditionRequiresSigner && !this.signer) { throw new Error( - `Condition contains ${USER_ADDRESS_PARAM} context variable and requires a signer to populate` + `Cannot use ${USER_ADDRESS_PARAM} as a parameter without a signer` ); } diff --git a/src/dkg.ts b/src/dkg.ts index 8fa9dd6ec..c5056c8f7 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -136,10 +136,10 @@ export class DkgClient { } public static async getFinalizedRitual( - web3Provider: ethers.providers.Web3Provider, + provider: ethers.providers.Provider, ritualId: number ): Promise { - const ritual = await DkgClient.getExistingRitual(web3Provider, ritualId); + const ritual = await DkgClient.getExistingRitual(provider, ritualId); if (ritual.state !== DkgRitualState.FINALIZED) { throw new Error( `Ritual ${ritualId} is not finalized. State: ${ritual.state}` diff --git a/src/taco.ts b/src/taco.ts index 434a587e7..6ea3c997d 100644 --- a/src/taco.ts +++ b/src/taco.ts @@ -1,19 +1,16 @@ -import { - Ciphertext, - DkgPublicKey, - ferveoEncrypt, -} from '@nucypher/nucypher-core'; +import { DkgPublicKey, ThresholdMessageKit } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; import { ThresholdDecrypter } from './characters/cbd-recipient'; +import { Enrico } from './characters/enrico'; import { Condition, ConditionExpression } from './conditions'; import { DkgClient } from './dkg'; import { getPorterUri } from './porter'; import { toBytes } from './utils'; export interface TacoMessageKit { - ciphertext: Ciphertext; - aad: Uint8Array; + thresholdMessageKit: ThresholdMessageKit; + conditionExpr: ConditionExpression; // TODO: How do we get rid of these two fields? We need them for decrypting // We ritualId in order to fetch the DKG participants and create DecryptionRequests for them ritualId: number; @@ -22,12 +19,12 @@ export interface TacoMessageKit { } export const encrypt = async ( - web3Provider: ethers.providers.Web3Provider, + provider: ethers.providers.Provider, message: string, condition: Condition, ritualId: number ): Promise => { - const dkgRitual = await DkgClient.getFinalizedRitual(web3Provider, ritualId); + const dkgRitual = await DkgClient.getFinalizedRitual(provider, ritualId); return await encryptLight( message, condition, @@ -45,19 +42,24 @@ export const encryptLight = async ( threshold: number, ritualId: number ): Promise => { - const aad = new ConditionExpression(condition).asAad(); - const ciphertext = ferveoEncrypt(toBytes(message), aad, dkgPublicKey); + const encrypter = new Enrico(dkgPublicKey); + const conditionExpr = new ConditionExpression(condition); + const thresholdMessageKit = await encrypter.encryptMessageCbd( + toBytes(message), + conditionExpr + ); return { - ciphertext, - aad, + thresholdMessageKit, threshold, ritualId, + conditionExpr, }; }; export const decrypt = async ( - web3Provider: ethers.providers.Web3Provider, + provider: ethers.providers.Provider, messageKit: TacoMessageKit, + signer?: ethers.Signer, porterUri = getPorterUri('tapir') ): Promise => { const decrypter = ThresholdDecrypter.create( @@ -65,15 +67,10 @@ export const decrypt = async ( messageKit.ritualId, messageKit.threshold ); - const condExpr = ConditionExpression.fromAad(messageKit.aad); - // TODO: We need web3Provider to fetch participants from Coordinator to make decryption requests. - // Removing this dependency is tied to release of ThresholdMessageKit - // Blocked by changes to nucypher-core and nucypher: - // https://github.com/nucypher/nucypher/pull/3194 return decrypter.retrieveAndDecrypt( - web3Provider, - condExpr, - messageKit.ciphertext + provider, + messageKit.thresholdMessageKit, + signer ); }; diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index 2dfc76fca..440fbd1b6 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -29,9 +29,9 @@ const { } = conditions; // Shared test variables -const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); -const aliceProvider = fakeProvider(aliceSecretKey.toBEBytes()); -const aliceSigner = fakeSigner(aliceSecretKey.toBEBytes()); +const secretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); +const provider = fakeProvider(secretKey.toBEBytes()); +const signer = fakeSigner(secretKey.toBEBytes()); const ownsNFT = new ERC721Ownership({ contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', parameters: [3591], @@ -56,7 +56,7 @@ async function makeDeployedCbdStrategy() { const getUrsulasSpy = mockGetUrsulas(ursulas); const getExistingRitualSpy = mockGetExistingRitual(mockedDkgRitual); - const deployedStrategy = await strategy.deploy(aliceProvider, ritualId); + const deployedStrategy = await strategy.deploy(provider, ritualId); expect(getUrsulasSpy).toHaveBeenCalled(); expect(getExistingRitualSpy).toHaveBeenCalled(); @@ -108,13 +108,13 @@ describe('CbdDeployedStrategy', () => { .encryptMessageCbd(message); // Setup mocks for `retrieveAndDecrypt` - const { decryptionShares } = await fakeTDecFlow({ + const { decryptionShares } = fakeTDecFlow({ ...mockedDkg, message: toBytes(message), dkgPublicKey: mockedDkg.dkg.publicKey(), thresholdMessageKit, }); - const { participantSecrets, participants } = await fakeDkgParticipants( + const { participantSecrets, participants } = fakeDkgParticipants( mockedDkg.ritualId ); const requesterSessionKey = SessionStaticSecret.random(); @@ -130,9 +130,9 @@ describe('CbdDeployedStrategy', () => { const decryptedMessage = await deployedStrategy.decrypter.retrieveAndDecrypt( - aliceProvider, + provider, thresholdMessageKit, - aliceSigner + signer ); expect(getUrsulasSpy).toHaveBeenCalled(); expect(getParticipantsSpy).toHaveBeenCalled(); diff --git a/test/unit/conditions/condition-expr.test.ts b/test/unit/conditions/condition-expr.test.ts index abdf09f5f..1ef17774a 100644 --- a/test/unit/conditions/condition-expr.test.ts +++ b/test/unit/conditions/condition-expr.test.ts @@ -221,50 +221,6 @@ describe('condition set', () => { } ); - it.each([ - // no "operator" nor "method" value - { - version: ConditionExpression.VERSION, - condition: { - randoKey: 'randoValue', - otherKey: 'otherValue', - }, - }, - // invalid "method" and no "contractAddress" - { - version: ConditionExpression.VERSION, - condition: { - method: 'doWhatIWant', - returnValueTest: { - index: 0, - comparator: '>', - value: '100', - }, - chain: 5, - }, - }, - // condition with wrong method "method" and no contract address - { - version: ConditionExpression.VERSION, - condition: { - ...testTimeConditionObj, - method: 'doWhatIWant', - }, - }, - // rpc condition (no contract address) with disallowed method - { - version: ConditionExpression.VERSION, - condition: { - ...testRpcConditionObj, - method: 'isPolicyActive', - }, - }, - ])("can't determine condition type", (invalidCondition) => { - expect(() => { - ConditionExpression.fromObj(invalidCondition); - }).toThrow('unrecognized condition data'); - }); - it.each(['_invalid_condition_type_', undefined as unknown as string])( 'rejects an invalid condition type', (invalidConditionType) => { diff --git a/test/unit/conditions/context.test.ts b/test/unit/conditions/context.test.ts index 4a0815912..d87c6291a 100644 --- a/test/unit/conditions/context.test.ts +++ b/test/unit/conditions/context.test.ts @@ -114,7 +114,7 @@ describe('context parameters', () => { const conditionExpr = new ConditionExpression(condition); expect(conditionExpr.contextRequiresSigner()).toBe(true); expect(() => conditionExpr.buildContext(provider, {}, undefined)).toThrow( - `Condition contains ${USER_ADDRESS_PARAM} context variable and requires a signer to populate` + `Cannot use ${USER_ADDRESS_PARAM} as a parameter without a signer` ); }); diff --git a/test/unit/taco.test.ts b/test/unit/taco.test.ts index 619620202..2158337ef 100644 --- a/test/unit/taco.test.ts +++ b/test/unit/taco.test.ts @@ -12,8 +12,9 @@ import { fakeDkgParticipants, fakeDkgRitual, fakePorterUri, + fakeProvider, + fakeSigner, fakeTDecFlow, - fakeWeb3Provider, mockCbdDecrypt, mockGetExistingRitual, mockGetParticipants, @@ -40,11 +41,12 @@ describe('taco', () => { it('encrypts and decrypts', async () => { const mockedDkg = fakeDkgFlow(variant, 0, 4, 4); const mockedDkgRitual = fakeDkgRitual(mockedDkg); - const web3Provider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); + const provider = fakeProvider(aliceSecretKey.toBEBytes()); + const signer = fakeSigner(aliceSecretKey.toBEBytes()); const getExistingRitualSpy = mockGetExistingRitual(mockedDkgRitual); const tacoMk = await taco.encrypt( - web3Provider, + provider, message, ownsNFT, mockedDkg.ritualId @@ -55,12 +57,11 @@ describe('taco', () => { const { decryptionShares } = fakeTDecFlow({ ...mockedDkg, message: toBytes(message), - aad: tacoMk.aad, - ciphertext: tacoMk.ciphertext, + dkgPublicKey: mockedDkg.dkg.publicKey(), + thresholdMessageKit: tacoMk.thresholdMessageKit, }); const { participantSecrets, participants } = fakeDkgParticipants( - mockedDkg.ritualId, - variant + mockedDkg.ritualId ); const requesterSessionKey = SessionStaticSecret.random(); const decryptSpy = mockCbdDecrypt( @@ -73,8 +74,9 @@ describe('taco', () => { const sessionKeySpy = mockRandomSessionStaticSecret(requesterSessionKey); const decryptedMessage = await taco.decrypt( - web3Provider, + provider, tacoMk, + signer, fakePorterUri ); expect(getParticipantsSpy).toHaveBeenCalled(); diff --git a/test/utils.ts b/test/utils.ts index e41b00144..1ce7ae461 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -313,7 +313,7 @@ interface FakeDkgRitualFlow { thresholdMessageKit: ThresholdMessageKit; } -export const fakeTDecFlow = async ({ +export const fakeTDecFlow = ({ validators, validatorKeypairs, ritualId, @@ -364,7 +364,7 @@ const fakeConditionExpr = () => { return new ConditionExpression(erc721Balance); }; -export const fakeDkgTDecFlowE2E = async ( +export const fakeDkgTDecFlowE2E = ( ritualId = 0, variant: FerveoVariant = FerveoVariant.precomputed, conditionExpr: ConditionExpression = fakeConditionExpr(), @@ -379,7 +379,7 @@ export const fakeDkgTDecFlowE2E = async ( conditionExpr ); - const { decryptionShares } = await fakeTDecFlow({ + const { decryptionShares } = fakeTDecFlow({ ...ritual, message, dkgPublicKey, @@ -429,13 +429,13 @@ export const fakeCoordinatorRitual = async ( }; }; -export const fakeDkgParticipants = async ( +export const fakeDkgParticipants = ( ritualId: number -): Promise<{ +): { participants: DkgParticipant[]; participantSecrets: Record; -}> => { - const ritual = await fakeDkgTDecFlowE2E(ritualId); +} => { + const ritual = fakeDkgTDecFlowE2E(ritualId); const label = toBytes(`${ritualId}`); const participantSecrets: Record = From 26a54e6dc6c24112e1edaf230e3fcf6f1faa73fc Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 5 Sep 2023 15:18:48 +0200 Subject: [PATCH 6/9] update coordinator contract --- abi/Coordinator.json | 905 ++++++++++++++++++-- src/agents/coordinator.ts | 45 +- src/dkg.ts | 21 +- src/sdk/strategy/cbd-strategy.ts | 4 +- src/taco.ts | 60 +- test/unit/cbd-strategy.test.ts | 4 +- test/unit/conditions/condition-expr.test.ts | 8 +- test/unit/taco.test.ts | 17 +- test/utils.ts | 12 +- 9 files changed, 950 insertions(+), 126 deletions(-) diff --git a/abi/Coordinator.json b/abi/Coordinator.json index c572ea259..a9f0143da 100644 --- a/abi/Coordinator.json +++ b/abi/Coordinator.json @@ -3,7 +3,7 @@ "inputs": [ { "internalType": "contract IAccessControlApplication", - "name": "app", + "name": "_stakes", "type": "address" }, { @@ -12,9 +12,24 @@ "type": "uint32" }, { - "internalType": "uint32", + "internalType": "uint16", "name": "_maxDkgSize", - "type": "uint32" + "type": "uint16" + }, + { + "internalType": "address", + "name": "_admin", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "_currency", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_feeRatePerSecond", + "type": "uint256" } ], "stateMutability": "nonpayable", @@ -45,21 +60,65 @@ "name": "AggregationPosted", "type": "event" }, + { + "anonymous": false, + "inputs": [], + "name": "DefaultAdminDelayChangeCanceled", + "type": "event" + }, { "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "uint32", - "name": "ritualId", - "type": "uint32" + "indexed": false, + "internalType": "uint48", + "name": "newDelay", + "type": "uint48" }, + { + "indexed": false, + "internalType": "uint48", + "name": "effectSchedule", + "type": "uint48" + } + ], + "name": "DefaultAdminDelayChangeScheduled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "DefaultAdminTransferCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ { "indexed": true, "internalType": "address", - "name": "initiator", + "name": "newAdmin", "type": "address" }, + { + "indexed": false, + "internalType": "uint48", + "name": "acceptSchedule", + "type": "uint48" + } + ], + "name": "DefaultAdminTransferScheduled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + }, { "indexed": false, "internalType": "bool", @@ -75,15 +134,15 @@ "inputs": [ { "indexed": false, - "internalType": "uint32", + "internalType": "uint16", "name": "oldSize", - "type": "uint32" + "type": "uint16" }, { "indexed": false, - "internalType": "uint32", + "internalType": "uint16", "name": "newSize", - "type": "uint32" + "type": "uint16" } ], "name": "MaxDkgSizeChanged", @@ -92,20 +151,118 @@ { "anonymous": false, "inputs": [ + { + "indexed": true, + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + }, + { + "indexed": true, + "internalType": "address", + "name": "participant", + "type": "address" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "word0", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "word1", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "word2", + "type": "bytes32" + } + ], + "indexed": false, + "internalType": "struct BLS12381.G2Point", + "name": "publicKey", + "type": "tuple" + } + ], + "name": "ParticipantPublicKeySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, { "indexed": true, "internalType": "address", - "name": "previousOwner", + "name": "account", "type": "address" }, { "indexed": true, "internalType": "address", - "name": "newOwner", + "name": "sender", "type": "address" } ], - "name": "OwnershipTransferred", + "name": "RoleRevoked", "type": "event" }, { @@ -133,7 +290,7 @@ { "indexed": true, "internalType": "address", - "name": "initiator", + "name": "authority", "type": "address" }, { @@ -190,6 +347,52 @@ "name": "TranscriptPosted", "type": "event" }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INITIATOR_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "TREASURY_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptDefaultAdminTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "application", @@ -203,6 +406,39 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "beginDefaultAdminTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "cancelDefaultAdminTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint48", + "name": "newDelay", + "type": "uint48" + } + ], + "name": "changeDefaultAdminDelay", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -223,11 +459,95 @@ "type": "function" }, { - "inputs": [ + "inputs": [], + "name": "currency", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "defaultAdmin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "defaultAdminDelay", + "outputs": [ + { + "internalType": "uint48", + "name": "", + "type": "uint48" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "defaultAdminDelayIncreaseWait", + "outputs": [ + { + "internalType": "uint48", + "name": "", + "type": "uint48" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeRatePerSecond", + "outputs": [ { "internalType": "uint256", - "name": "ritualID", + "name": "", "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + } + ], + "name": "getAuthority", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" }, { "internalType": "address", @@ -312,17 +632,271 @@ { "inputs": [ { - "internalType": "uint256", - "name": "ritualId", - "type": "uint256" + "internalType": "address", + "name": "_provider", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_ritualId", + "type": "uint256" + } + ], + "name": "getProviderPublicKey", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "word0", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "word1", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "word2", + "type": "bytes32" + } + ], + "internalType": "struct BLS12381.G2Point", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + } + ], + "name": "getPublicKeyFromRitualId", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "word0", + "type": "bytes32" + }, + { + "internalType": "bytes16", + "name": "word1", + "type": "bytes16" + } + ], + "internalType": "struct BLS12381.G1Point", + "name": "dkgPublicKey", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "word0", + "type": "bytes32" + }, + { + "internalType": "bytes16", + "name": "word1", + "type": "bytes16" + } + ], + "internalType": "struct BLS12381.G1Point", + "name": "dkgPublicKey", + "type": "tuple" + } + ], + "name": "getRitualIdFromPublicKey", + "outputs": [ + { + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "providers", + "type": "address[]" + }, + { + "internalType": "uint32", + "name": "duration", + "type": "uint32" + } + ], + "name": "getRitualInitiationCost", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + } + ], + "name": "getRitualState", + "outputs": [ + { + "internalType": "enum Coordinator.RitualState", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "size", + "type": "uint16" + } + ], + "name": "getThresholdForRitualSize", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "providers", + "type": "address[]" + }, + { + "internalType": "address", + "name": "authority", + "type": "address" + }, + { + "internalType": "uint32", + "name": "duration", + "type": "uint32" + }, + { + "internalType": "contract IEncryptionAuthorizer", + "name": "accessController", + "type": "address" } ], - "name": "getRitualState", + "name": "initiateRitual", "outputs": [ { - "internalType": "enum Coordinator.RitualState", + "internalType": "uint32", "name": "", - "type": "uint8" + "type": "uint32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isInitiationPublic", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" } ], "stateMutability": "view", @@ -331,19 +905,26 @@ { "inputs": [ { - "internalType": "address[]", - "name": "providers", - "type": "address[]" + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" } ], - "name": "initiateRitual", + "name": "isRitualFinalized", "outputs": [ { - "internalType": "uint32", + "internalType": "bool", "name": "", - "type": "uint32" + "type": "bool" } ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "makeInitiationPublic", + "outputs": [], "stateMutability": "nonpayable", "type": "function" }, @@ -352,9 +933,9 @@ "name": "maxDkgSize", "outputs": [ { - "internalType": "uint32", + "internalType": "uint16", "name": "", - "type": "uint32" + "type": "uint16" } ], "stateMutability": "view", @@ -386,6 +967,61 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "pendingDefaultAdmin", + "outputs": [ + { + "internalType": "address", + "name": "newAdmin", + "type": "address" + }, + { + "internalType": "uint48", + "name": "schedule", + "type": "uint48" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingDefaultAdminDelay", + "outputs": [ + { + "internalType": "uint48", + "name": "newDelay", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "schedule", + "type": "uint48" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "pendingFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -412,7 +1048,7 @@ } ], "internalType": "struct BLS12381.G1Point", - "name": "publicKey", + "name": "dkgPublicKey", "type": "tuple" }, { @@ -445,8 +1081,50 @@ "type": "function" }, { - "inputs": [], - "name": "renounceOwnership", + "inputs": [ + { + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + } + ], + "name": "processPendingFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -468,23 +1146,48 @@ }, { "internalType": "uint32", - "name": "dkgSize", + "name": "initTimestamp", "type": "uint32" }, { "internalType": "uint32", - "name": "initTimestamp", + "name": "endTimestamp", "type": "uint32" }, { - "internalType": "uint32", + "internalType": "uint16", "name": "totalTranscripts", - "type": "uint32" + "type": "uint16" }, { - "internalType": "uint32", + "internalType": "uint16", "name": "totalAggregations", - "type": "uint32" + "type": "uint16" + }, + { + "internalType": "address", + "name": "authority", + "type": "address" + }, + { + "internalType": "uint16", + "name": "dkgSize", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "threshold", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "aggregationMismatch", + "type": "bool" + }, + { + "internalType": "contract IEncryptionAuthorizer", + "name": "accessController", + "type": "address" }, { "components": [ @@ -503,11 +1206,6 @@ "name": "publicKey", "type": "tuple" }, - { - "internalType": "bool", - "name": "aggregationMismatch", - "type": "bool" - }, { "internalType": "bytes", "name": "aggregatedTranscript", @@ -517,12 +1215,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "rollbackDefaultAdminDelay", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { - "internalType": "uint32", + "internalType": "uint16", "name": "newSize", - "type": "uint32" + "type": "uint16" } ], "name": "setMaxDkgSize", @@ -530,6 +1235,67 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "word0", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "word1", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "word2", + "type": "bytes32" + } + ], + "internalType": "struct BLS12381.G2Point", + "name": "_publicKey", + "type": "tuple" + } + ], + "name": "setProviderPublicKey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IReimbursementPool", + "name": "pool", + "type": "address" + } + ], + "name": "setReimbursementPool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + }, + { + "internalType": "address", + "name": "authority", + "type": "address" + } + ], + "name": "setRitualAuthority", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -543,6 +1309,25 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "timeout", @@ -556,15 +1341,33 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "totalPendingFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { - "internalType": "address", - "name": "newOwner", + "internalType": "contract IERC20", + "name": "token", "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" } ], - "name": "transferOwnership", + "name": "withdrawTokens", "outputs": [], "stateMutability": "nonpayable", "type": "function" diff --git a/src/agents/coordinator.ts b/src/agents/coordinator.ts index db8378d8d..28ed866cc 100644 --- a/src/agents/coordinator.ts +++ b/src/agents/coordinator.ts @@ -1,5 +1,5 @@ -import { SessionStaticKey } from '@nucypher/nucypher-core'; -import { ethers } from 'ethers'; +import { DkgPublicKey, SessionStaticKey } from '@nucypher/nucypher-core'; +import { BigNumberish, ethers } from 'ethers'; import { Coordinator, @@ -13,12 +13,16 @@ import { DEFAULT_WAIT_N_CONFIRMATIONS, getContract } from './contracts'; export interface CoordinatorRitual { initiator: string; - dkgSize: number; initTimestamp: number; + endTimestamp: number; totalTranscripts: number; totalAggregations: number; - publicKey: BLS12381.G1PointStructOutput; + authority: string; + dkgSize: number; + threshold: number; aggregationMismatch: boolean; + accessController: string; + publicKey: BLS12381.G1PointStructOutput; aggregatedTranscript: string; } @@ -59,10 +63,18 @@ export class DkgCoordinatorAgent { public static async initializeRitual( provider: ethers.providers.Provider, signer: ethers.Signer, - providers: ChecksumAddress[] + providers: ChecksumAddress[], + authority: string, + duration: BigNumberish, + accessController: string ): Promise { const Coordinator = await this.connectReadWrite(provider, signer); - const tx = await Coordinator.initiateRitual(providers); + const tx = await Coordinator.initiateRitual( + providers, + authority, + duration, + accessController + ); const txReceipt = await tx.wait(DEFAULT_WAIT_N_CONFIRMATIONS); const [ritualStartEvent] = txReceipt.events ?? []; if (!ritualStartEvent) { @@ -95,16 +107,25 @@ export class DkgCoordinatorAgent { const Coordinator = await this.connectReadOnly(provider); // We leave `initiator` undefined because we don't care who the initiator is // We leave `successful` undefined because we don't care if the ritual was successful - const eventFilter = Coordinator.filters.EndRitual( - ritualId, - undefined, - undefined - ); - Coordinator.once(eventFilter, (_ritualId, _initiator, successful) => { + const eventFilter = Coordinator.filters.EndRitual(ritualId, undefined); + Coordinator.once(eventFilter, (_ritualId, successful) => { callback(successful); }); } + public static async getRitualIdFromPublicKey( + provider: ethers.providers.Provider, + dkgPublicKey: DkgPublicKey + ): Promise { + const Coordinator = await this.connectReadOnly(provider); + const dkgPublicKeyBytes = dkgPublicKey.toBytes(); + const pointStruct: BLS12381.G1PointStruct = { + word0: dkgPublicKeyBytes.slice(0, 16), + word1: dkgPublicKeyBytes.slice(16, 32), + }; + return await Coordinator.getRitualIdFromPublicKey(pointStruct); + } + private static async connectReadOnly(provider: ethers.providers.Provider) { return await this.connect(provider); } diff --git a/src/dkg.ts b/src/dkg.ts index c5056c8f7..b907158ad 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -1,5 +1,5 @@ import { DkgPublicKey } from '@nucypher/nucypher-core'; -import { ethers } from 'ethers'; +import { BigNumberish, ethers } from 'ethers'; import { DkgCoordinatorAgent, DkgRitualState } from './agents/coordinator'; import { ChecksumAddress } from './types'; @@ -58,22 +58,23 @@ export class DkgRitual { } } -// TODO: Currently, we're assuming that the threshold is always `floor(sharesNum / 2) + 1`. -// https://github.com/nucypher/nucypher/issues/3095 -const assumedThreshold = (sharesNum: number): number => - Math.floor(sharesNum / 2) + 1; - export class DkgClient { public static async initializeRitual( provider: ethers.providers.Provider, signer: ethers.Signer, ursulas: ChecksumAddress[], + authority: string, + duration: BigNumberish, + accessController: string, waitUntilEnd = false ): Promise { const ritualId = await DkgCoordinatorAgent.initializeRitual( provider, signer, - ursulas.sort() + ursulas.sort(), + authority, + duration, + accessController ); if (waitUntilEnd) { @@ -111,7 +112,7 @@ export class DkgClient { }); }; - public static async getExistingRitual( + public static async getRitual( provider: ethers.providers.Provider, ritualId: number ): Promise { @@ -129,7 +130,7 @@ export class DkgClient { DkgPublicKey.fromBytes(dkgPkBytes), { sharesNum: ritual.dkgSize, - threshold: assumedThreshold(ritual.dkgSize), + threshold: ritual.threshold, }, ritualState ); @@ -139,7 +140,7 @@ export class DkgClient { provider: ethers.providers.Provider, ritualId: number ): Promise { - const ritual = await DkgClient.getExistingRitual(provider, ritualId); + const ritual = await DkgClient.getRitual(provider, ritualId); if (ritual.state !== DkgRitualState.FINALIZED) { throw new Error( `Ritual ${ritualId} is not finalized. State: ${ritual.state}` diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index ed0a37d8f..ed27e24f7 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -44,7 +44,7 @@ export class CbdStrategy { // // Given that we just initialized the ritual, this should never happen // throw new Error('Ritual ID is undefined'); // } - const dkgRitual = await DkgClient.getExistingRitual(provider, ritualId); + const dkgRitual = await DkgClient.getRitual(provider, ritualId); return DeployedCbdStrategy.create(dkgRitual, this.cohort.porterUri); } @@ -92,7 +92,7 @@ export class DeployedCbdStrategy { porterUri: string, ritualId: number ): Promise { - const dkgRitual = await DkgClient.getExistingRitual(provider, ritualId); + const dkgRitual = await DkgClient.getRitual(provider, ritualId); return DeployedCbdStrategy.create(dkgRitual, porterUri); } diff --git a/src/taco.ts b/src/taco.ts index 6ea3c997d..6ded5a372 100644 --- a/src/taco.ts +++ b/src/taco.ts @@ -1,6 +1,7 @@ import { DkgPublicKey, ThresholdMessageKit } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; +import { DkgCoordinatorAgent } from './agents/coordinator'; import { ThresholdDecrypter } from './characters/cbd-recipient'; import { Enrico } from './characters/enrico'; import { Condition, ConditionExpression } from './conditions'; @@ -8,70 +9,47 @@ import { DkgClient } from './dkg'; import { getPorterUri } from './porter'; import { toBytes } from './utils'; -export interface TacoMessageKit { - thresholdMessageKit: ThresholdMessageKit; - conditionExpr: ConditionExpression; - // TODO: How do we get rid of these two fields? We need them for decrypting - // We ritualId in order to fetch the DKG participants and create DecryptionRequests for them - ritualId: number; - // We need to know the threshold in order to create DecryptionRequests - threshold: number; -} - export const encrypt = async ( provider: ethers.providers.Provider, message: string, condition: Condition, ritualId: number -): Promise => { +): Promise => { const dkgRitual = await DkgClient.getFinalizedRitual(provider, ritualId); - return await encryptLight( - message, - condition, - dkgRitual.dkgPublicKey, - dkgRitual.dkgParams.threshold, - ritualId - ); + return await encryptLight(message, condition, dkgRitual.dkgPublicKey); }; export const encryptLight = async ( message: string, condition: Condition, - dkgPublicKey: DkgPublicKey, - // TODO: Remove these parameters after fixing TacoMessageKit - threshold: number, - ritualId: number -): Promise => { + dkgPublicKey: DkgPublicKey +): Promise => { const encrypter = new Enrico(dkgPublicKey); const conditionExpr = new ConditionExpression(condition); - const thresholdMessageKit = await encrypter.encryptMessageCbd( - toBytes(message), - conditionExpr - ); - return { - thresholdMessageKit, - threshold, - ritualId, - conditionExpr, - }; + return encrypter.encryptMessageCbd(toBytes(message), conditionExpr); }; export const decrypt = async ( provider: ethers.providers.Provider, - messageKit: TacoMessageKit, + messageKit: ThresholdMessageKit, signer?: ethers.Signer, porterUri = getPorterUri('tapir') ): Promise => { + const ritualId = await DkgCoordinatorAgent.getRitualIdFromPublicKey( + provider, + messageKit.acp.publicKey + ); + const ritual = await DkgClient.getFinalizedRitual(provider, ritualId); const decrypter = ThresholdDecrypter.create( porterUri, - messageKit.ritualId, - messageKit.threshold - ); - return decrypter.retrieveAndDecrypt( - provider, - messageKit.thresholdMessageKit, - signer + ritualId, + ritual.dkgParams.threshold ); + // TODO: What do we do if there are no conditions? + if (!messageKit.acp.conditions) { + throw new Error('ThresholdMessageKit does not contain conditions'); + } + return decrypter.retrieveAndDecrypt(provider, messageKit, signer); }; export const taco = { diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index 440fbd1b6..51810e531 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -15,8 +15,8 @@ import { fakeUrsulas, makeCohort, mockCbdDecrypt, - mockGetExistingRitual, mockGetParticipants, + mockGetRitual, mockGetUrsulas, mockRandomSessionStaticSecret, } from '../utils'; @@ -54,7 +54,7 @@ async function makeDeployedCbdStrategy() { const mockedDkg = fakeDkgFlow(variant, 0, 4, 4); const mockedDkgRitual = fakeDkgRitual(mockedDkg); const getUrsulasSpy = mockGetUrsulas(ursulas); - const getExistingRitualSpy = mockGetExistingRitual(mockedDkgRitual); + const getExistingRitualSpy = mockGetRitual(mockedDkgRitual); const deployedStrategy = await strategy.deploy(provider, ritualId); diff --git a/test/unit/conditions/condition-expr.test.ts b/test/unit/conditions/condition-expr.test.ts index 1ef17774a..6941f834d 100644 --- a/test/unit/conditions/condition-expr.test.ts +++ b/test/unit/conditions/condition-expr.test.ts @@ -9,7 +9,7 @@ import { TimeCondition, TimeConditionProps, } from '../../../src/conditions/base'; -import { RpcConditionType } from '../../../src/conditions/base/rpc'; +import { RpcConditionType } from '../../../src/conditions/base'; import { USER_ADDRESS_PARAM } from '../../../src/conditions/const'; import { ERC721Balance } from '../../../src/conditions/predefined'; import { objectEquals, toJSON } from '../../../src/utils'; @@ -187,6 +187,12 @@ describe('condition set', () => { ConditionExpression.fromJSON(conditionExprJson); expect(conditionExprFromJson).toBeDefined(); expect(conditionExprFromJson.equals(conditionExprFromJson)).toBeTruthy(); + + const asWasmConditions = conditionExprFromJson.toWASMConditions(); + const fromWasmConditions = + ConditionExpression.fromWASMConditions(asWasmConditions); + expect(fromWasmConditions).toBeDefined(); + expect(fromWasmConditions.equals(conditionExprFromJson)).toBeTruthy(); }); it('serializes to and from WASM conditions', () => { diff --git a/test/unit/taco.test.ts b/test/unit/taco.test.ts index 2158337ef..2648abff9 100644 --- a/test/unit/taco.test.ts +++ b/test/unit/taco.test.ts @@ -16,8 +16,9 @@ import { fakeSigner, fakeTDecFlow, mockCbdDecrypt, - mockGetExistingRitual, mockGetParticipants, + mockGetRitual, + mockGetRitualIdFromPublicKey, mockRandomSessionStaticSecret, } from '../utils'; @@ -43,9 +44,9 @@ describe('taco', () => { const mockedDkgRitual = fakeDkgRitual(mockedDkg); const provider = fakeProvider(aliceSecretKey.toBEBytes()); const signer = fakeSigner(aliceSecretKey.toBEBytes()); - const getExistingRitualSpy = mockGetExistingRitual(mockedDkgRitual); + const getExistingRitualSpy = mockGetRitual(mockedDkgRitual); - const tacoMk = await taco.encrypt( + const messageKit = await taco.encrypt( provider, message, ownsNFT, @@ -58,7 +59,7 @@ describe('taco', () => { ...mockedDkg, message: toBytes(message), dkgPublicKey: mockedDkg.dkg.publicKey(), - thresholdMessageKit: tacoMk.thresholdMessageKit, + thresholdMessageKit: messageKit, }); const { participantSecrets, participants } = fakeDkgParticipants( mockedDkg.ritualId @@ -72,15 +73,21 @@ describe('taco', () => { ); const getParticipantsSpy = mockGetParticipants(participants); const sessionKeySpy = mockRandomSessionStaticSecret(requesterSessionKey); + const getRitualIdFromPublicKey = mockGetRitualIdFromPublicKey( + mockedDkg.ritualId + ); + const getRitualSpy = mockGetRitual(mockedDkgRitual); const decryptedMessage = await taco.decrypt( provider, - tacoMk, + messageKit, signer, fakePorterUri ); expect(getParticipantsSpy).toHaveBeenCalled(); expect(sessionKeySpy).toHaveBeenCalled(); + expect(getRitualIdFromPublicKey).toHaveBeenCalled(); + expect(getRitualSpy).toHaveBeenCalled(); expect(decryptSpy).toHaveBeenCalled(); expect(decryptedMessage).toEqual(toBytes(message)); }); diff --git a/test/utils.ts b/test/utils.ts index 1ce7ae461..066ee5d61 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -524,12 +524,20 @@ export const fakeDkgRitual = (ritual: { ); }; -export const mockGetExistingRitual = (dkgRitual: DkgRitual) => { - return jest.spyOn(DkgClient, 'getExistingRitual').mockImplementation(() => { +export const mockGetRitual = (dkgRitual: DkgRitual) => { + return jest.spyOn(DkgClient, 'getRitual').mockImplementation(() => { return Promise.resolve(dkgRitual); }); }; +export const mockGetRitualIdFromPublicKey = (ritualId: number) => { + return jest + .spyOn(DkgCoordinatorAgent, 'getRitualIdFromPublicKey') + .mockImplementation(() => { + return Promise.resolve(ritualId); + }); +}; + export const makeCohort = async (ursulas: Ursula[]) => { const getUrsulasSpy = mockGetUrsulas(ursulas); const porterUri = 'https://_this.should.crash'; From 19a73b7124ca0d87cff3d6858576ff4e5d50cb08 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 6 Sep 2023 11:08:57 +0200 Subject: [PATCH 7/9] update after rebase --- src/taco.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/taco.ts b/src/taco.ts index 6ded5a372..7b5e15876 100644 --- a/src/taco.ts +++ b/src/taco.ts @@ -45,10 +45,6 @@ export const decrypt = async ( ritualId, ritual.dkgParams.threshold ); - // TODO: What do we do if there are no conditions? - if (!messageKit.acp.conditions) { - throw new Error('ThresholdMessageKit does not contain conditions'); - } return decrypter.retrieveAndDecrypt(provider, messageKit, signer); }; From 1dba09f9ea713e2eca43a5bbe30558d1e889c08b Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Fri, 8 Sep 2023 19:18:55 +0200 Subject: [PATCH 8/9] apply pr suggestions --- src/agents/coordinator.ts | 4 ++-- src/conditions/context/context.ts | 2 +- test/unit/conditions/context.test.ts | 8 +++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/agents/coordinator.ts b/src/agents/coordinator.ts index 28ed866cc..3f480e698 100644 --- a/src/agents/coordinator.ts +++ b/src/agents/coordinator.ts @@ -120,8 +120,8 @@ export class DkgCoordinatorAgent { const Coordinator = await this.connectReadOnly(provider); const dkgPublicKeyBytes = dkgPublicKey.toBytes(); const pointStruct: BLS12381.G1PointStruct = { - word0: dkgPublicKeyBytes.slice(0, 16), - word1: dkgPublicKeyBytes.slice(16, 32), + word0: dkgPublicKeyBytes.slice(0, 32), + word1: dkgPublicKeyBytes.slice(32, 48), }; return await Coordinator.getRitualIdFromPublicKey(pointStruct); } diff --git a/src/conditions/context/context.ts b/src/conditions/context/context.ts index 0b64cad90..feeeb257c 100644 --- a/src/conditions/context/context.ts +++ b/src/conditions/context/context.ts @@ -51,7 +51,7 @@ export class ConditionContext { ); if (conditionRequiresSigner && !this.signer) { throw new Error( - `Cannot use ${USER_ADDRESS_PARAM} as a parameter without a signer` + `Signer required to satisfy ${USER_ADDRESS_PARAM} context variable in condition` ); } diff --git a/test/unit/conditions/context.test.ts b/test/unit/conditions/context.test.ts index d87c6291a..dc629c7a5 100644 --- a/test/unit/conditions/context.test.ts +++ b/test/unit/conditions/context.test.ts @@ -91,6 +91,10 @@ describe('context parameters', () => { const condition = new ContractCondition(conditionObj); const conditionExpr = new ConditionExpression(condition); expect(conditionExpr.contextRequiresSigner()).toBe(true); + expect(conditionExpr.buildContext(provider, {}, signer)).toBeDefined(); + expect(() => conditionExpr.buildContext(provider, {})).toThrow( + `Signer required to satisfy ${USER_ADDRESS_PARAM} context variable in condition` + ); }); it('detects if a signer is not required', () => { @@ -100,6 +104,8 @@ describe('context parameters', () => { false ); expect(conditionExpr.contextRequiresSigner()).toBe(false); + expect(conditionExpr.buildContext(provider, {}, signer)).toBeDefined(); + expect(conditionExpr.buildContext(provider, {})).toBeDefined(); }); it('rejects on a missing signer', () => { @@ -114,7 +120,7 @@ describe('context parameters', () => { const conditionExpr = new ConditionExpression(condition); expect(conditionExpr.contextRequiresSigner()).toBe(true); expect(() => conditionExpr.buildContext(provider, {}, undefined)).toThrow( - `Cannot use ${USER_ADDRESS_PARAM} as a parameter without a signer` + `Signer required to satisfy ${USER_ADDRESS_PARAM} context variable in condition` ); }); From e1a0cd1508a67765b5e0e9544180c20c08e0aa84 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 12 Sep 2023 15:53:00 +0200 Subject: [PATCH 9/9] remove redundant DkgRitualParameters struct --- src/dkg.ts | 31 +++++++++++++++---------------- src/sdk/strategy/cbd-strategy.ts | 2 +- src/taco.ts | 2 +- test/utils.ts | 6 ++---- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/dkg.ts b/src/dkg.ts index b907158ad..479213f3e 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -3,17 +3,13 @@ import { BigNumberish, ethers } from 'ethers'; import { DkgCoordinatorAgent, DkgRitualState } from './agents/coordinator'; import { ChecksumAddress } from './types'; -import { fromHexString, objectEquals } from './utils'; - -export type DkgRitualParameters = { - sharesNum: number; - threshold: number; -}; +import { fromHexString } from './utils'; export interface DkgRitualJSON { id: number; dkgPublicKey: Uint8Array; - dkgParams: DkgRitualParameters; + sharesNum: number; + threshold: number; state: DkgRitualState; } @@ -21,7 +17,8 @@ export class DkgRitual { constructor( public readonly id: number, public readonly dkgPublicKey: DkgPublicKey, - public readonly dkgParams: DkgRitualParameters, + public readonly sharesNum: number, + public readonly threshold: number, public readonly state: DkgRitualState ) {} @@ -29,7 +26,8 @@ export class DkgRitual { return { id: this.id, dkgPublicKey: this.dkgPublicKey.toBytes(), - dkgParams: this.dkgParams, + sharesNum: this.sharesNum, + threshold: this.threshold, state: this.state, }; } @@ -37,13 +35,15 @@ export class DkgRitual { public static fromObj({ id, dkgPublicKey, - dkgParams, + sharesNum, + threshold, state, }: DkgRitualJSON): DkgRitual { return new DkgRitual( id, DkgPublicKey.fromBytes(dkgPublicKey), - dkgParams, + sharesNum, + threshold, state ); } @@ -52,7 +52,8 @@ export class DkgRitual { return [ this.id === other.id, this.dkgPublicKey.equals(other.dkgPublicKey), - objectEquals(this.dkgParams, other.dkgParams), + this.sharesNum === other.sharesNum, + this.threshold === other.threshold, this.state === other.state, ].every(Boolean); } @@ -128,10 +129,8 @@ export class DkgClient { return new DkgRitual( ritualId, DkgPublicKey.fromBytes(dkgPkBytes), - { - sharesNum: ritual.dkgSize, - threshold: ritual.threshold, - }, + ritual.dkgSize, + ritual.threshold, ritualState ); } diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index ed27e24f7..acca9c3f7 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -81,7 +81,7 @@ export class DeployedCbdStrategy { const decrypter = ThresholdDecrypter.create( porterUri, dkgRitual.id, - dkgRitual.dkgParams.threshold + dkgRitual.threshold ); return new DeployedCbdStrategy(decrypter, dkgRitual.dkgPublicKey); } diff --git a/src/taco.ts b/src/taco.ts index 7b5e15876..13034bef7 100644 --- a/src/taco.ts +++ b/src/taco.ts @@ -43,7 +43,7 @@ export const decrypt = async ( const decrypter = ThresholdDecrypter.create( porterUri, ritualId, - ritual.dkgParams.threshold + ritual.threshold ); return decrypter.retrieveAndDecrypt(provider, messageKit, signer); }; diff --git a/test/utils.ts b/test/utils.ts index 066ee5d61..ec8b1aeee 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -516,10 +516,8 @@ export const fakeDkgRitual = (ritual: { return new DkgRitual( fakeRitualId, ritual.dkg.publicKey(), - { - sharesNum: ritual.sharesNum, - threshold: ritual.threshold, - }, + ritual.sharesNum, + ritual.threshold, DkgRitualState.FINALIZED ); };