Skip to content

Commit

Permalink
feat: implement local ritual verificaiton
Browse files Browse the repository at this point in the history
  • Loading branch information
piotr-roslaniec committed Aug 10, 2023
1 parent 6f4cc7a commit a4a956f
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 23 deletions.
1 change: 1 addition & 0 deletions src/agents/coordinator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface CoordinatorRitual {
export type DkgParticipant = {
provider: string;
aggregated: boolean;
// TODO: How do I get the transcript from the Coordinator?
transcript: Transcript;
decryptionRequestStaticKey: SessionStaticKey;
};
Expand Down
8 changes: 5 additions & 3 deletions src/agents/subscription-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class PreSubscriptionManagerAgent {
}

public static async getPolicyCost(
provider: ethers.providers.Provider,
provider: ethers.providers.Web3Provider,
size: number,
startTimestamp: number,
endTimestamp: number
Expand All @@ -61,7 +61,9 @@ export class PreSubscriptionManagerAgent {
);
}

private static async connectReadOnly(provider: ethers.providers.Provider) {
private static async connectReadOnly(
provider: ethers.providers.Web3Provider
) {
return await this.connect(provider);
}

Expand All @@ -72,7 +74,7 @@ export class PreSubscriptionManagerAgent {
}

private static async connect(
provider: ethers.providers.Provider,
provider: ethers.providers.Web3Provider,
signer?: ethers.providers.JsonRpcSigner
): Promise<SubscriptionManager> {
const network = await provider.getNetwork();
Expand Down
11 changes: 11 additions & 0 deletions src/characters/cbd-recipient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '../agents/coordinator';
import { ConditionExpression } from '../conditions';
import {
DkgClient,
DkgRitual,
getCombineDecryptionSharesFunction,
getVariantClass,
Expand Down Expand Up @@ -91,6 +92,16 @@ export class ThresholdDecrypter {
);
}

const isLocallyVerified = await DkgClient.verifyRitual(
web3Provider,
this.ritualId
);
if (!isLocallyVerified) {
throw new Error(
`Ritual with id ${this.ritualId} has failed local verification.`
);
}

const dkgParticipants = await DkgCoordinatorAgent.getParticipants(
web3Provider,
this.ritualId
Expand Down
70 changes: 50 additions & 20 deletions src/dkg.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import {
AggregatedTranscript,
combineDecryptionSharesPrecomputed,
combineDecryptionSharesSimple,
DecryptionSharePrecomputed,
DecryptionShareSimple,
DkgPublicKey,
EthereumAddress,
FerveoPublicKey,
FerveoVariant,
SharedSecret,
Validator,
ValidatorMessage,
} from '@nucypher/nucypher-core';
import { ethers } from 'ethers';

Expand Down Expand Up @@ -97,6 +102,21 @@ export class DkgRitual {
const assumedThreshold = (sharesNum: number): number =>
Math.floor(sharesNum / 2) + 1;

// TODO: Without Validator public key in Coordinator, we cannot verify the
// transcript. We need to add it to the Coordinator (nucypher-contracts #77).
const participantPublicKeys: Record<string, FerveoPublicKey> = {
'0x210eeAC07542F815ebB6FD6689637D8cA2689392': FerveoPublicKey.fromBytes(
fromHexString(
'ace9d7567b26dafc512b2303cfdaa872850c62b100078ddeaabf8408c7308b3a43dfeb88375c21ef63230fb4008ce7e908764463c6765e556f9b03009eb1757d179eaa26bf875332807cc070d62a385ed2e66e09f4f4766451da12779a09036e'
)
),
'0xb15d5A4e2be34f4bE154A1b08a94Ab920FfD8A41': FerveoPublicKey.fromBytes(
fromHexString(
'8b373fdb6b43e9dca028bd603c2bf90f0e008ec83ff217a8d7bc006b585570e6ab1ce761bad0e21c1aed1363286145f61134ed0ab53f4ebaa05036396c57f6e587f33d49667c1003cd03b71ad651b09dd4791bc631eaef93f1b313bbee7bd63a'
)
),
};

export class DkgClient {
public static async initializeRitual(
web3Provider: ethers.providers.Web3Provider,
Expand Down Expand Up @@ -167,24 +187,34 @@ export class DkgClient {
);
}

// 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<boolean> {
// const ritual = await DkgCoordinatorAgent.getRitual(this.provider, ritualId);
// const participants = await DkgCoordinatorAgent.getParticipants(
// this.provider,
// ritualId
// );
//
// const validatorMessages = participants.map((p) => {
// const validatorAddress = EthereumAddress.fromString(p.provider);
// const publicKey = FerveoPublicKey.fromBytes(fromHexString(p.???));
// const validator = new Validator(validatorAddress, publicKey);
// const transcript = Transcript.fromBytes(fromHexString(p.transcript));
// return new ValidatorMessage(validator, transcript);
// });
// const aggregate = new AggregatedTranscript(validatorMessages);
//
// return aggregate.verify(ritual.dkgSize, validatorMessages);
// }
public static async verifyRitual(
web3Provider: ethers.providers.Web3Provider,
ritualId: number
): Promise<boolean> {
const ritual = await DkgCoordinatorAgent.getRitual(web3Provider, ritualId);
const participants = await DkgCoordinatorAgent.getParticipants(
web3Provider,
ritualId
);

// TODO: Does this check make sense here? Or do we delegate it to the Coordinator contract?
// for (const p of participants) {
// // Not every participant has submitted a transcript
// if (!p.aggregated) {
// return false;
// }
// }

const validatorMessages = participants.map((p) => {
const validatorAddress = EthereumAddress.fromString(p.provider);
// TODO: Replace with real keys
// const publicKey = FerveoPublicKey.fromBytes(fromHexString(p.???));
const publicKey = participantPublicKeys[p.provider];
const validator = new Validator(validatorAddress, publicKey);
return new ValidatorMessage(validator, p.transcript);
});
const aggregate = new AggregatedTranscript(validatorMessages);

return aggregate.verify(ritual.dkgSize, validatorMessages);
}
}
4 changes: 4 additions & 0 deletions test/unit/cbd-strategy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
mockGetUrsulas,
mockInitializeRitual,
mockRandomSessionStaticSecret,
mockVerifyRitual,
} from '../utils';

import { aliceSecretKeyBytes } from './testVariables';
Expand Down Expand Up @@ -133,6 +134,7 @@ describe('CbdDeployedStrategy', () => {
const getUrsulasSpy = mockGetUrsulas(ursulas);
const sessionKeySpy = mockRandomSessionStaticSecret(requesterSessionKey);
const getRitualStateSpy = mockGetRitualState();
const verifyRitualSpy = mockVerifyRitual();

const decryptedMessage =
await deployedStrategy.decrypter.retrieveAndDecrypt(
Expand All @@ -141,6 +143,8 @@ describe('CbdDeployedStrategy', () => {
variant,
ciphertext
);
expect(getRitualStateSpy).toHaveBeenCalled();
expect(verifyRitualSpy).toHaveBeenCalled();
expect(getUrsulasSpy).toHaveBeenCalled();
expect(getParticipantsSpy).toHaveBeenCalled();
expect(sessionKeySpy).toHaveBeenCalled();
Expand Down
6 changes: 6 additions & 0 deletions test/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -546,3 +546,9 @@ export const mockGetRitualState = (state = DkgRitualState.FINALIZED) => {
.spyOn(DkgCoordinatorAgent, 'getRitualState')
.mockImplementation((_provider, _ritualId) => Promise.resolve(state));
};

export const mockVerifyRitual = (isValid = true) => {
return jest
.spyOn(DkgClient, 'verifyRitual')
.mockImplementation((_provider, _ritualId) => Promise.resolve(isValid));
};

0 comments on commit a4a956f

Please sign in to comment.