diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index cef8d360c..939b25ccd 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -24,13 +24,13 @@ import { fromJSON, toJSON } from '../utils'; import { Porter } from './porter'; -export type CbdTDecDecrypterJSON = { +export type ThresholdDecrypterJSON = { porterUri: string; ritualId: number; threshold: number; }; -export class CbdTDecDecrypter { +export class ThresholdDecrypter { // private readonly verifyingKey: Keyring; private constructor( @@ -40,7 +40,7 @@ export class CbdTDecDecrypter { ) {} public static create(porterUri: string, dkgRitual: DkgRitual) { - return new CbdTDecDecrypter( + return new ThresholdDecrypter( new Porter(porterUri), dkgRitual.id, dkgRitual.threshold @@ -193,7 +193,7 @@ export class CbdTDecDecrypter { return SessionStaticSecret.random(); } - public toObj(): CbdTDecDecrypterJSON { + public toObj(): ThresholdDecrypterJSON { return { porterUri: this.porter.porterUrl.toString(), ritualId: this.ritualId, @@ -209,15 +209,15 @@ export class CbdTDecDecrypter { porterUri, ritualId, threshold, - }: CbdTDecDecrypterJSON) { - return new CbdTDecDecrypter(new Porter(porterUri), ritualId, threshold); + }: ThresholdDecrypterJSON) { + return new ThresholdDecrypter(new Porter(porterUri), ritualId, threshold); } public static fromJSON(json: string) { - return CbdTDecDecrypter.fromObj(fromJSON(json)); + return ThresholdDecrypter.fromObj(fromJSON(json)); } - public equals(other: CbdTDecDecrypter): boolean { + public equals(other: ThresholdDecrypter): boolean { return ( this.porter.porterUrl.toString() === other.porter.porterUrl.toString() ); diff --git a/src/characters/pre-recipient.ts b/src/characters/pre-recipient.ts index ae0fc8cda..c30f9265f 100644 --- a/src/characters/pre-recipient.ts +++ b/src/characters/pre-recipient.ts @@ -16,7 +16,7 @@ import { base64ToU8Receiver, bytesEquals, toJSON, zip } from '../utils'; import { Porter } from './porter'; -export type PreTDecDecrypterJSON = { +export type PreDecrypterJSON = { porterUri: string; policyEncryptingKeyBytes: Uint8Array; encryptedTreasureMapBytes: Uint8Array; @@ -24,7 +24,7 @@ export type PreTDecDecrypterJSON = { bobSecretKeyBytes: Uint8Array; }; -export class PreTDecDecrypter { +export class PreDecrypter { // private readonly verifyingKey: Keyring; constructor( @@ -41,8 +41,8 @@ export class PreTDecDecrypter { policyEncryptingKey: PublicKey, publisherVerifyingKey: PublicKey, encryptedTreasureMap: EncryptedTreasureMap - ): PreTDecDecrypter { - return new PreTDecDecrypter( + ): PreDecrypter { + return new PreDecrypter( new Porter(porterUri), new Keyring(secretKey), policyEncryptingKey, @@ -149,7 +149,7 @@ export class PreTDecDecrypter { }); } - public toObj(): PreTDecDecrypterJSON { + public toObj(): PreDecrypterJSON { return { porterUri: this.porter.porterUrl.toString(), policyEncryptingKeyBytes: this.policyEncryptingKey.toCompressedBytes(), @@ -170,8 +170,8 @@ export class PreTDecDecrypter { encryptedTreasureMapBytes, publisherVerifyingKeyBytes, bobSecretKeyBytes, - }: PreTDecDecrypterJSON) { - return new PreTDecDecrypter( + }: PreDecrypterJSON) { + return new PreDecrypter( new Porter(porterUri), new Keyring(SecretKey.fromBEBytes(bobSecretKeyBytes)), PublicKey.fromCompressedBytes(policyEncryptingKeyBytes), @@ -182,10 +182,10 @@ export class PreTDecDecrypter { public static fromJSON(json: string) { const config = JSON.parse(json, base64ToU8Receiver); - return PreTDecDecrypter.fromObj(config); + return PreDecrypter.fromObj(config); } - public equals(other: PreTDecDecrypter): boolean { + public equals(other: PreDecrypter): boolean { return ( this.porter.porterUrl.toString() === other.porter.porterUrl.toString() && this.policyEncryptingKey.equals(other.policyEncryptingKey) && diff --git a/src/dkg.ts b/src/dkg.ts index f0ee81e41..35405c44d 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -87,12 +87,12 @@ export class DkgRitual { export class DkgClient { constructor(private readonly provider: ethers.providers.Web3Provider) {} - // TODO: Update API: Replace with getExistingRitual and support ritualId in Strategy - public async initializeRitual(ritualParams: { - shares: number; - threshold: number; - }): Promise { + public async initializeRitual(): Promise { const ritualId = 2; + return this.getExistingRitual(ritualId); + } + + public async getExistingRitual(ritualId: number): Promise { const ritual = await DkgCoordinatorAgent.getRitual(this.provider, ritualId); const dkgPkBytes = new Uint8Array([ ...fromHexString(ritual.publicKey.word0), @@ -102,7 +102,7 @@ export class DkgClient { return { id: ritualId, dkgPublicKey: DkgPublicKey.fromBytes(dkgPkBytes), - threshold: ritualParams.threshold, + threshold: ritual.dkgSize, // TODO: dkgSize is not the threshold, but in this case shares == threshold == dkgSize } as DkgRitual; } diff --git a/src/index.ts b/src/index.ts index b8caa482b..12d769eff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,7 @@ export { Alice } from './characters/alice'; export { Bob, RemoteBob } from './characters/bob'; export { Enrico } from './characters/enrico'; -export { PreTDecDecrypter } from './characters/pre-recipient'; +export { PreDecrypter } from './characters/pre-recipient'; export { Porter } from './characters/porter'; // Policies diff --git a/src/policies/policy.ts b/src/policies/policy.ts index 1ccca6a90..59be31dd0 100644 --- a/src/policies/policy.ts +++ b/src/policies/policy.ts @@ -28,15 +28,6 @@ export type EnactedPolicy = { type IPreEnactedPolicy = Omit; -export type EnactedPolicyJSON = Omit< - EnactedPolicy, - 'policyKey' | 'encryptedTreasureMap' | 'id' -> & { - policyKey: Uint8Array; - id: Uint8Array; - encryptedTreasureMap: Uint8Array; -}; - export class PreEnactedPolicy implements IPreEnactedPolicy { constructor( public readonly id: HRAC, @@ -138,18 +129,18 @@ export class BlockchainPolicy { ursulas: readonly Ursula[], verifiedKFrags: readonly VerifiedKeyFrag[] ): TreasureMap { - const assigned_kfrags: [Address, [PublicKey, VerifiedKeyFrag]][] = []; + const assignedKFrags: [Address, [PublicKey, VerifiedKeyFrag]][] = []; zip(ursulas, verifiedKFrags).forEach(([ursula, kFrag]) => { const ursulaAddress = new Address( toCanonicalAddress(ursula.checksumAddress) ); - assigned_kfrags.push([ursulaAddress, [ursula.encryptingKey, kFrag]]); + assignedKFrags.push([ursulaAddress, [ursula.encryptingKey, kFrag]]); }); return new TreasureMap( this.publisher.signer, this.hrac, this.delegatingKey, - assigned_kfrags, + assignedKFrags, this.threshold ); } diff --git a/src/sdk/cohort.ts b/src/sdk/cohort.ts index 8571f3816..697f7cb8a 100644 --- a/src/sdk/cohort.ts +++ b/src/sdk/cohort.ts @@ -2,41 +2,33 @@ import { Porter } from '../characters/porter'; import { ChecksumAddress } from '../types'; import { objectEquals } from '../utils'; -export type CohortConfiguration = { - readonly threshold: number; - readonly shares: number; - readonly porterUri: string; -}; - export type CohortJSON = { ursulaAddresses: ChecksumAddress[]; - threshold: number; - shares: number; porterUri: string; }; export class Cohort { private constructor( - public ursulaAddresses: ChecksumAddress[], - public readonly configuration: CohortConfiguration + public readonly ursulaAddresses: ChecksumAddress[], + public readonly porterUri: string ) {} public static async create( - configuration: CohortConfiguration, + porterUri: string, + numUrsulas: number, include: string[] = [], exclude: string[] = [] ) { - const porter = new Porter(configuration.porterUri); - const ursulas = await porter.getUrsulas( - configuration.shares, - exclude, - include.splice(0, configuration.shares) - ); + const porter = new Porter(porterUri); + const ursulas = await porter.getUrsulas(numUrsulas, exclude, include); const ursulaAddresses = ursulas.map((ursula) => ursula.checksumAddress); - return new Cohort(ursulaAddresses, configuration); + return new Cohort(ursulaAddresses, porterUri); } - public get shares(): number { + public static fromUrsulas(ursulas: ChecksumAddress[], porterUri: string) { + return new Cohort(ursulas, porterUri); + } + public get numUrsulas(): number { return this.ursulaAddresses.length; } @@ -49,26 +41,14 @@ export class Cohort { return Cohort.fromObj(config); } - public static fromObj({ - ursulaAddresses, - threshold, - shares, - porterUri, - }: CohortJSON) { - const config = { - threshold: threshold, - shares: shares, - porterUri: porterUri, - }; - return new Cohort(ursulaAddresses, config); + public static fromObj({ ursulaAddresses, porterUri }: CohortJSON) { + return new Cohort(ursulaAddresses, porterUri); } public toObj(): CohortJSON { return { ursulaAddresses: this.ursulaAddresses, - threshold: this.configuration.threshold, - shares: this.configuration.shares, - porterUri: this.configuration.porterUri, + porterUri: this.porterUri, }; } diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index 0632eeaef..b53dac400 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -3,8 +3,8 @@ import { ethers } from 'ethers'; import { bytesEqual } from '../../../test/utils'; import { - CbdTDecDecrypter, - CbdTDecDecrypterJSON, + ThresholdDecrypter, + ThresholdDecrypterJSON, } from '../../characters/cbd-recipient'; import { Enrico } from '../../characters/enrico'; import { ConditionExpression, ConditionExpressionJSON } from '../../conditions'; @@ -18,7 +18,7 @@ export type CbdStrategyJSON = { }; export type DeployedStrategyJSON = { - decrypter: CbdTDecDecrypterJSON; + decrypter: ThresholdDecrypterJSON; dkgPublicKey: Uint8Array; }; @@ -32,13 +32,9 @@ export class CbdStrategy { public async deploy( provider: ethers.providers.Web3Provider ): Promise { - const dkgRitualParams = { - threshold: this.cohort.configuration.threshold, - shares: this.cohort.configuration.shares, - }; const dkgClient = new DkgClient(provider); - const dkgRitual = await dkgClient.initializeRitual(dkgRitualParams); - return DeployedCbdStrategy.create(this.cohort, dkgRitual); + const dkgRitual = await dkgClient.initializeRitual(); + return DeployedCbdStrategy.create(dkgRitual, this.cohort.porterUri); } public static fromJSON(json: string) { @@ -66,18 +62,26 @@ export class CbdStrategy { export class DeployedCbdStrategy { private constructor( - public readonly decrypter: CbdTDecDecrypter, + public readonly decrypter: ThresholdDecrypter, public readonly dkgPublicKey: DkgPublicKey ) {} - public static create(cohort: Cohort, dkgRitual: DkgRitual) { - const decrypter = CbdTDecDecrypter.create( - cohort.configuration.porterUri, - dkgRitual - ); + public static create(dkgRitual: DkgRitual, porterUri: string) { + const decrypter = ThresholdDecrypter.create(porterUri, dkgRitual); return new DeployedCbdStrategy(decrypter, dkgRitual.dkgPublicKey); } + // TODO: This is analogous to create() above, is it useful? + public static async fromRitualId( + provider: ethers.providers.Web3Provider, + porterUri: string, + ritualId: number + ): Promise { + const dkgClient = new DkgClient(provider); + const dkgRitual = await dkgClient.getExistingRitual(ritualId); + return DeployedCbdStrategy.create(dkgRitual, porterUri); + } + public makeEncrypter(conditionExpr: ConditionExpression): Enrico { return new Enrico(this.dkgPublicKey, undefined, conditionExpr); } @@ -93,7 +97,7 @@ export class DeployedCbdStrategy { private static fromObj({ decrypter, dkgPublicKey }: DeployedStrategyJSON) { return new DeployedCbdStrategy( - CbdTDecDecrypter.fromObj(decrypter), + ThresholdDecrypter.fromObj(decrypter), DkgPublicKey.fromBytes(dkgPublicKey) ); } diff --git a/src/sdk/strategy/pre-strategy.ts b/src/sdk/strategy/pre-strategy.ts index acc6d997f..01b8f96ac 100644 --- a/src/sdk/strategy/pre-strategy.ts +++ b/src/sdk/strategy/pre-strategy.ts @@ -4,10 +4,7 @@ import { ethers } from 'ethers'; import { Alice } from '../../characters/alice'; import { Bob } from '../../characters/bob'; import { Enrico } from '../../characters/enrico'; -import { - PreTDecDecrypter, - PreTDecDecrypterJSON, -} from '../../characters/pre-recipient'; +import { PreDecrypter, PreDecrypterJSON } from '../../characters/pre-recipient'; import { ConditionExpression } from '../../conditions'; import { EnactedPolicy } from '../../policies/policy'; import { base64ToU8Receiver, bytesEquals, toJSON } from '../../utils'; @@ -23,7 +20,7 @@ export type PreStrategyJSON = { export type DeployedPreStrategyJSON = { cohortConfig: CohortJSON; - decrypterJSON: PreTDecDecrypterJSON; + decrypterJSON: PreDecrypterJSON; policyKeyBytes: Uint8Array; }; @@ -65,10 +62,20 @@ export class PreStrategy { } public async deploy( + provider: ethers.providers.Web3Provider, label: string, - provider: ethers.providers.Web3Provider + threshold?: number, + shares?: number ): Promise { - const porterUri = this.cohort.configuration.porterUri; + shares = shares || this.cohort.numUrsulas; + threshold = threshold || Math.floor(shares / 2) + 1; + if (shares > this.cohort.numUrsulas) { + throw new Error( + `Threshold ${threshold} cannot be greater than the number of Ursulas in the cohort ${this.cohort.numUrsulas}` + ); + } + + const porterUri = this.cohort.porterUri; const configuration = { porterUri }; const alice = Alice.fromSecretKey( configuration, @@ -79,8 +86,8 @@ export class PreStrategy { const policyParams = { bob, label, - threshold: this.cohort.configuration.threshold, - shares: this.cohort.configuration.shares, + threshold, + shares, startDate: this.startDate, endDate: this.endDate, }; @@ -146,7 +153,7 @@ export class PreStrategy { export class DeployedPreStrategy { private constructor( public readonly cohort: Cohort, - public readonly decrypter: PreTDecDecrypter, + public readonly decrypter: PreDecrypter, public readonly policyKey: PublicKey ) {} @@ -155,8 +162,8 @@ export class DeployedPreStrategy { policy: EnactedPolicy, bobSecretKey: SecretKey ) { - const decrypter = PreTDecDecrypter.create( - cohort.configuration.porterUri, + const decrypter = PreDecrypter.create( + cohort.porterUri, bobSecretKey, policy.policyKey, policy.aliceVerifyingKey, @@ -184,7 +191,7 @@ export class DeployedPreStrategy { policyKeyBytes, }: DeployedPreStrategyJSON) { const cohort = Cohort.fromObj(cohortConfig); - const decrypter = PreTDecDecrypter.fromObj(decrypterJSON); + const decrypter = PreDecrypter.fromObj(decrypterJSON); const policyKey = PublicKey.fromCompressedBytes(policyKeyBytes); return new DeployedPreStrategy(cohort, decrypter, policyKey); } diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index cac579632..217a45ca0 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -60,12 +60,9 @@ describe('Get Started (CBD PoC)', () => { // // 2. Build a Cohort - const config = { - threshold: 3, - shares: 5, - porterUri: 'https://porter-tapir.nucypher.community', - }; - const newCohort = await Cohort.create(config); + const porterUri = 'https://porter-tapir.nucypher.community'; + const numUrsulas = 5; + const newCohort = await Cohort.create(porterUri, numUrsulas); // 3. Specify default conditions const NFTOwnership = new ERC721Ownership({ @@ -86,7 +83,7 @@ describe('Get Started (CBD PoC)', () => { const mumbai = providers.getNetwork(80001); const web3Provider = new providers.Web3Provider(MMprovider, mumbai); - const newDeployed = await newStrategy.deploy('test', web3Provider); + const newDeployed = await newStrategy.deploy(web3Provider, 'test'); // 5. Encrypt the plaintext & update conditions const NFTBalanceConfig = { diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index 36a8f216b..8186e24ad 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -3,7 +3,7 @@ import { SecretKey, SessionStaticSecret } from '@nucypher/nucypher-core'; import { conditions } from '../../src'; import { FerveoVariant } from '../../src'; import { CbdStrategy, DeployedCbdStrategy } from '../../src'; -import { CbdTDecDecrypter } from '../../src/characters/cbd-recipient'; +import { ThresholdDecrypter } from '../../src/characters/cbd-recipient'; import { toBytes } from '../../src/utils'; import { fakeDkgFlow, @@ -163,14 +163,14 @@ describe('CbdTDecDecrypter', () => { it('serializes to a plain object', async () => { const { deployedStrategy } = await makeDeployedCbdStrategy(); const configObj = deployedStrategy.decrypter.toObj(); - const fromObj = CbdTDecDecrypter.fromObj(configObj); + const fromObj = ThresholdDecrypter.fromObj(configObj); expect(fromObj.equals(deployedStrategy.decrypter)).toBeTruthy(); }); it('serializes to a JSON', async () => { const { deployedStrategy } = await makeDeployedCbdStrategy(); const configJSON = deployedStrategy.decrypter.toJSON(); - const fromJSON = CbdTDecDecrypter.fromJSON(configJSON); + const fromJSON = ThresholdDecrypter.fromJSON(configJSON); expect(fromJSON.equals(deployedStrategy.decrypter)).toBeTruthy(); }); }); diff --git a/test/unit/pre-strategy.test.ts b/test/unit/pre-strategy.test.ts index 9fa599a22..bdd8ace8f 100644 --- a/test/unit/pre-strategy.test.ts +++ b/test/unit/pre-strategy.test.ts @@ -3,8 +3,8 @@ import { SecretKey, VerifiedKeyFrag } from '@nucypher/nucypher-core'; import { conditions, DeployedPreStrategy, + PreDecrypter, PreStrategy, - PreTDecDecrypter, } from '../../src'; import { Ursula } from '../../src/characters/porter'; import { toBytes } from '../../src/utils'; @@ -54,7 +54,7 @@ const makeDeployedPreStrategy = async () => { const makeTreasureMapSpy = mockMakeTreasureMap(); const encryptTreasureMapSpy = mockEncryptTreasureMap(); - const deployedStrategy = await strategy.deploy('test', aliceProvider); + const deployedStrategy = await strategy.deploy(aliceProvider, 'test'); expect(generateKFragsSpy).toHaveBeenCalled(); expect(publishToBlockchainSpy).toHaveBeenCalled(); @@ -155,14 +155,14 @@ describe('PreTDecDecrypter', () => { it('serializes to a plain object', async () => { const { deployedStrategy } = await makeDeployedPreStrategy(); const asObj = deployedStrategy.decrypter.toObj(); - const fromJson = PreTDecDecrypter.fromObj(asObj); + const fromJson = PreDecrypter.fromObj(asObj); expect(fromJson.equals(deployedStrategy.decrypter)).toBeTruthy(); }); it('serializes to JSON', async () => { const { deployedStrategy } = await makeDeployedPreStrategy(); const asJson = deployedStrategy.decrypter.toJSON(); - const fromJson = PreTDecDecrypter.fromJSON(asJson); + const fromJson = PreDecrypter.fromJSON(asJson); expect(fromJson.equals(deployedStrategy.decrypter)).toBeTruthy(); }); }); diff --git a/test/utils.ts b/test/utils.ts index b79ca46d6..3e3ec881d 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -39,7 +39,7 @@ import { keccak256 } from 'ethers/lib/utils'; import { Alice, Bob, Cohort, Configuration, RemoteBob } from '../src'; import { DkgCoordinatorAgent, DkgParticipant } from '../src/agents/coordinator'; -import { CbdTDecDecrypter } from '../src/characters/cbd-recipient'; +import { ThresholdDecrypter } from '../src/characters/cbd-recipient'; import { CbdDecryptResult, GetUrsulasResult, @@ -490,7 +490,7 @@ export const mockCbdDecrypt = ( export const mockRandomSessionStaticSecret = (secret: SessionStaticSecret) => { return jest - .spyOn(CbdTDecDecrypter.prototype as any, 'makeSessionKey') + .spyOn(ThresholdDecrypter.prototype as any, 'makeSessionKey') .mockImplementation(() => secret); }; @@ -510,12 +510,9 @@ export const mockInitializeRitual = (fakeRitual: unknown) => { export const makeCohort = async (ursulas: Ursula[]) => { const getUrsulasSpy = mockGetUrsulas(ursulas); - const config = { - threshold: 2, - shares: 3, - porterUri: 'https://_this.should.crash', - }; - const cohort = await Cohort.create(config); + const porterUri = 'https://_this.should.crash'; + const numUrsulas = ursulas.length; + const cohort = await Cohort.create(porterUri, numUrsulas); expect(getUrsulasSpy).toHaveBeenCalled(); return cohort; };