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 ab68104b3..fad36b183 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,69 +9,53 @@ 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 + 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'); + } + const conditionExpr = ConditionExpression.fromWASMConditions( + messageKit.acp.conditions ); return decrypter.retrieveAndDecrypt( provider, - messageKit.conditionExpr, - messageKit.thresholdMessageKit, + conditionExpr, + messageKit, signer ); }; diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index 06add8912..96ebf908a 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'; @@ -55,7 +55,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 dc197e4b5..4aa493c07 100644 --- a/test/unit/conditions/condition-expr.test.ts +++ b/test/unit/conditions/condition-expr.test.ts @@ -186,6 +186,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 bfe6ffc1f..8873f9b53 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, @@ -57,9 +58,9 @@ describe('taco', () => { const { decryptionShares } = fakeTDecFlow({ ...mockedDkg, message: toBytes(message), - conditionExpr: tacoMk.conditionExpr, + conditionExpr: new conditions.ConditionExpression(ownsNFT), dkgPublicKey: mockedDkg.dkg.publicKey(), - thresholdMessageKit: tacoMk.thresholdMessageKit, + thresholdMessageKit: messageKit, }); const { participantSecrets, participants } = fakeDkgParticipants( mockedDkg.ritualId @@ -73,15 +74,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 9b3f0f6c1..8780d1e03 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -526,12 +526,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';