diff --git a/.gitignore b/.gitignore index 421b9b5ba..7ebab6644 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ examples/*/pnpm-lock.yaml pnpm-debug.log docs-json ./docs +.next diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index 92ac6853f..1d88e022a 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -9,7 +9,7 @@ "type-check": "tsc -p tsconfig.build.json" }, "dependencies": { - "@nucypher/shared": "workspace:*", + "@nucypher/pre": "workspace:*", "@types/node": "20.6.3", "@types/react": "18.2.22", "@types/react-dom": "18.2.7", @@ -22,7 +22,6 @@ "typescript": "5.2.2" }, "peerDependencies": { - "@nucypher/shared": "workspace:*", "ethers": "^5.7.2", "typescript": "5.2.2" } diff --git a/examples/nextjs/src/app/page.tsx b/examples/nextjs/src/app/page.tsx index 31d9300c9..40f879420 100644 --- a/examples/nextjs/src/app/page.tsx +++ b/examples/nextjs/src/app/page.tsx @@ -7,7 +7,7 @@ import { initialize, SecretKey, toHexString, -} from '@nucypher/shared'; +} from '@nucypher/pre'; import {ethers} from 'ethers'; import {useEffect, useState} from 'react'; diff --git a/examples/nodejs/package.json b/examples/nodejs/package.json index a6dc3930c..a7d3826e9 100644 --- a/examples/nodejs/package.json +++ b/examples/nodejs/package.json @@ -9,7 +9,9 @@ "type-check": "tsc" }, "dependencies": { - "@nucypher/shared": "workspace:*", + "@nucypher/pre": "workspace:*" + }, + "peerDependencies": { "ethers": "^5.7.2" } } diff --git a/examples/nodejs/src/index.ts b/examples/nodejs/src/index.ts index ed6680dff..7320e2269 100644 --- a/examples/nodejs/src/index.ts +++ b/examples/nodejs/src/index.ts @@ -5,7 +5,7 @@ import { initialize, SecretKey, toBytes, -} from '@nucypher/shared'; +} from '@nucypher/pre'; import { ethers } from 'ethers'; const makeAlice = () => { diff --git a/examples/pre/nextjs/next-env.d.ts b/examples/pre/nextjs/next-env.d.ts new file mode 100644 index 000000000..4f11a03dc --- /dev/null +++ b/examples/pre/nextjs/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/examples/react/package.json b/examples/react/package.json index cefe60b36..513cd7e60 100644 --- a/examples/react/package.json +++ b/examples/react/package.json @@ -23,8 +23,7 @@ ] }, "dependencies": { - "@nucypher/shared": "workspace:*", - "ethers": "^5.7.2", + "@nucypher/pre": "workspace:*", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -33,5 +32,8 @@ "@types/react": "^18.2.21", "@types/react-dom": "^18.2.7", "react-scripts": "^5.0.1" + }, + "peerDependencies": { + "ethers": "^5.7.2" } } diff --git a/examples/react/src/App.tsx b/examples/react/src/App.tsx index c08220141..a9f106c87 100644 --- a/examples/react/src/App.tsx +++ b/examples/react/src/App.tsx @@ -6,7 +6,7 @@ import { initialize, SecretKey, toHexString, -} from '@nucypher/shared'; +} from '@nucypher/pre'; import { ethers } from 'ethers'; import { useEffect, useState } from 'react'; diff --git a/examples/taco/nextjs/next-env.d.ts b/examples/taco/nextjs/next-env.d.ts new file mode 100644 index 000000000..4f11a03dc --- /dev/null +++ b/examples/taco/nextjs/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/examples/webpack-5/package.json b/examples/webpack-5/package.json index c07997c2b..640d02886 100644 --- a/examples/webpack-5/package.json +++ b/examples/webpack-5/package.json @@ -11,8 +11,7 @@ "type-check": "tsc" }, "dependencies": { - "@nucypher/shared": "workspace:*", - "ethers": "^5.7.2" + "@nucypher/pre": "workspace:*" }, "devDependencies": { "copy-webpack-plugin": "^10.2.4", @@ -20,5 +19,8 @@ "webpack": "^5.4.0", "webpack-cli": "^4.9.2", "webpack-dev-server": "^4.7.4" + }, + "peerDependencies": { + "ethers": "^5.7.2" } } diff --git a/examples/webpack-5/src/index.ts b/examples/webpack-5/src/index.ts index 057317a03..4fa122103 100644 --- a/examples/webpack-5/src/index.ts +++ b/examples/webpack-5/src/index.ts @@ -1,10 +1,4 @@ -import { - Alice, - Bob, - SecretKey, - getPorterUri, - initialize, -} from '@nucypher/shared'; +import { Alice, Bob, SecretKey, getPorterUri, initialize } from '@nucypher/pre'; import { ethers } from 'ethers'; declare global { diff --git a/packages/shared/src/characters/alice.ts b/packages/pre/src/characters/alice.ts similarity index 97% rename from packages/shared/src/characters/alice.ts rename to packages/pre/src/characters/alice.ts index c93d3b262..2bc07d1cb 100644 --- a/packages/shared/src/characters/alice.ts +++ b/packages/pre/src/characters/alice.ts @@ -4,6 +4,7 @@ import { Signer, VerifiedKeyFrag, } from '@nucypher/nucypher-core'; +import { ChecksumAddress, PorterClient } from '@nucypher/shared'; import { ethers } from 'ethers'; import { Keyring } from '../keyring'; @@ -13,8 +14,6 @@ import { EnactedPolicy, PreEnactedPolicy, } from '../policy'; -import { PorterClient } from '../porter'; -import { ChecksumAddress } from '../types'; import { RemoteBob } from './bob'; diff --git a/packages/shared/src/characters/bob.ts b/packages/pre/src/characters/bob.ts similarity index 98% rename from packages/shared/src/characters/bob.ts rename to packages/pre/src/characters/bob.ts index 5fbe2f5b7..6fac22ba7 100644 --- a/packages/shared/src/characters/bob.ts +++ b/packages/pre/src/characters/bob.ts @@ -5,11 +5,10 @@ import { SecretKey, Signer, } from '@nucypher/nucypher-core'; +import { PorterClient, zip } from '@nucypher/shared'; import { Keyring } from '../keyring'; import { PolicyMessageKit, RetrievalResult } from '../kits'; -import { PorterClient } from '../porter'; -import { zip } from '../utils'; export class RemoteBob { private constructor( diff --git a/packages/pre/src/characters/enrico.ts b/packages/pre/src/characters/enrico.ts new file mode 100644 index 000000000..a617ffc97 --- /dev/null +++ b/packages/pre/src/characters/enrico.ts @@ -0,0 +1,39 @@ +import { MessageKit, PublicKey, SecretKey } from '@nucypher/nucypher-core'; +import { ConditionExpression, toBytes } from '@nucypher/shared'; + +import { Keyring } from '../keyring'; + +export class Enrico { + public readonly encryptingKey: PublicKey; + private readonly keyring: Keyring; + public conditions?: ConditionExpression | undefined; + + constructor( + encryptingKey: PublicKey, + verifyingKey?: SecretKey, + conditions?: ConditionExpression, + ) { + this.encryptingKey = encryptingKey; + this.keyring = new Keyring(verifyingKey ?? SecretKey.random()); + this.conditions = conditions; + } + + public get verifyingKey(): PublicKey { + return this.keyring.publicKey; + } + + public encryptMessagePre( + plaintext: Uint8Array | string, + withConditions?: ConditionExpression, + ): MessageKit { + if (!withConditions) { + withConditions = this.conditions; + } + + return new MessageKit( + this.encryptingKey, + plaintext instanceof Uint8Array ? plaintext : toBytes(plaintext), + withConditions ? withConditions.toWASMConditions() : null, + ); + } +} diff --git a/packages/shared/src/characters/index.ts b/packages/pre/src/characters/index.ts similarity index 69% rename from packages/shared/src/characters/index.ts rename to packages/pre/src/characters/index.ts index add1486ba..ab6a5bad7 100644 --- a/packages/shared/src/characters/index.ts +++ b/packages/pre/src/characters/index.ts @@ -1,3 +1,4 @@ export * from './alice'; export * from './bob'; export * from './enrico'; +export * from './pre-recipient'; diff --git a/packages/pre/src/pre-recipient.ts b/packages/pre/src/characters/pre-recipient.ts similarity index 98% rename from packages/pre/src/pre-recipient.ts rename to packages/pre/src/characters/pre-recipient.ts index 5bc68b9e5..ab46b9443 100644 --- a/packages/pre/src/pre-recipient.ts +++ b/packages/pre/src/characters/pre-recipient.ts @@ -10,15 +10,15 @@ import { base64ToU8Receiver, ConditionContext, ConditionExpression, - Keyring, - PolicyMessageKit, PorterClient, - RetrievalResult, toJSON, zip, } from '@nucypher/shared'; import { ethers } from 'ethers'; +import { Keyring } from '../keyring'; +import { PolicyMessageKit, RetrievalResult } from '../kits'; + export type PreDecrypterJSON = { porterUri: string; policyEncryptingKeyBytes: Uint8Array; diff --git a/packages/pre/src/index.ts b/packages/pre/src/index.ts index 69cd90846..ca4deb863 100644 --- a/packages/pre/src/index.ts +++ b/packages/pre/src/index.ts @@ -1,21 +1,11 @@ -// TODO: Create a pre module and export it here -// Similarly to how taco works -// export {pre} from './pre'; -// What goes into the pre module? Should we re-export the basic building blocks and/or remake the helper methods? export { - Alice, - BlockchainPolicyParameters, - Bob, Cohort, - EnactedPolicy, - Enrico, - Keyring, - PolicyMessageKit, PorterClient, - PreEnactedPolicy, - RemoteBob, conditions, + fromHexString, getPorterUri, + toBytes, + toHexString, } from '@nucypher/shared'; export { @@ -27,8 +17,11 @@ export { SecretKey, Signer, TreasureMap, + initialize, } from '@nucypher/nucypher-core'; export { DeployedPreStrategy, PreStrategy } from './pre-strategy'; -export { PreDecrypter } from './pre-recipient'; +export { Alice, Bob, PreDecrypter } from './characters'; + +export { EnactedPolicy } from './policy'; diff --git a/packages/shared/src/keyring.ts b/packages/pre/src/keyring.ts similarity index 97% rename from packages/shared/src/keyring.ts rename to packages/pre/src/keyring.ts index 813247501..53b02b446 100644 --- a/packages/shared/src/keyring.ts +++ b/packages/pre/src/keyring.ts @@ -7,9 +7,9 @@ import { Signer, VerifiedKeyFrag, } from '@nucypher/nucypher-core'; +import { toBytes } from '@nucypher/shared'; import { PolicyMessageKit } from './kits'; -import { toBytes } from './utils'; export class Keyring { constructor(public readonly secretKey: SecretKey) {} diff --git a/packages/shared/src/kits/index.ts b/packages/pre/src/kits/index.ts similarity index 100% rename from packages/shared/src/kits/index.ts rename to packages/pre/src/kits/index.ts diff --git a/packages/shared/src/kits/message.ts b/packages/pre/src/kits/message.ts similarity index 97% rename from packages/shared/src/kits/message.ts rename to packages/pre/src/kits/message.ts index 8ae9ab7b6..bcb9bd36a 100644 --- a/packages/shared/src/kits/message.ts +++ b/packages/pre/src/kits/message.ts @@ -5,8 +5,7 @@ import { RetrievalKit, SecretKey, } from '@nucypher/nucypher-core'; - -import { ChecksumAddress } from '../types'; +import { ChecksumAddress } from '@nucypher/shared'; import { RetrievalResult } from './retrieval'; diff --git a/packages/shared/src/kits/retrieval.ts b/packages/pre/src/kits/retrieval.ts similarity index 92% rename from packages/shared/src/kits/retrieval.ts rename to packages/pre/src/kits/retrieval.ts index 4fb1216de..ad5f333e2 100644 --- a/packages/shared/src/kits/retrieval.ts +++ b/packages/pre/src/kits/retrieval.ts @@ -1,6 +1,5 @@ import { VerifiedCapsuleFrag } from '@nucypher/nucypher-core'; - -import { ChecksumAddress } from '../types'; +import { ChecksumAddress } from '@nucypher/shared'; export class RetrievalResult { constructor( diff --git a/packages/shared/src/policy.ts b/packages/pre/src/policy.ts similarity index 96% rename from packages/shared/src/policy.ts rename to packages/pre/src/policy.ts index 754f2037d..2494b9977 100644 --- a/packages/shared/src/policy.ts +++ b/packages/pre/src/policy.ts @@ -6,13 +6,17 @@ import { TreasureMap, VerifiedKeyFrag, } from '@nucypher/nucypher-core'; +import { + PreSubscriptionManagerAgent, + toBytes, + toCanonicalAddress, + toEpoch, + Ursula, + zip, +} from '@nucypher/shared'; import { ethers } from 'ethers'; import { Alice, RemoteBob } from './characters'; -import { PreSubscriptionManagerAgent } from './contracts'; -import { Ursula } from './porter'; -import { toBytes, toEpoch, zip } from './utils'; -import { toCanonicalAddress } from './web3'; export type EnactedPolicy = { readonly id: HRAC; diff --git a/packages/pre/src/pre-strategy.ts b/packages/pre/src/pre-strategy.ts index e02ab5f73..b2e7fec0c 100644 --- a/packages/pre/src/pre-strategy.ts +++ b/packages/pre/src/pre-strategy.ts @@ -1,18 +1,21 @@ import { PublicKey, SecretKey } from '@nucypher/nucypher-core'; import { - Alice, base64ToU8Receiver, - Bob, Cohort, CohortJSON, ConditionExpression, - EnactedPolicy, - Enrico, toJSON, } from '@nucypher/shared'; import { ethers } from 'ethers'; -import { PreDecrypter, PreDecrypterJSON } from './pre-recipient'; +import { + Alice, + Bob, + Enrico, + PreDecrypter, + PreDecrypterJSON, +} from './characters'; +import { EnactedPolicy } from './policy'; export type PreStrategyJSON = { cohort: CohortJSON; diff --git a/packages/shared/test/integration/enrico.test.ts b/packages/pre/test/enrico.test.ts similarity index 99% rename from packages/shared/test/integration/enrico.test.ts rename to packages/pre/test/enrico.test.ts index 96487cb2e..42dfe924f 100644 --- a/packages/shared/test/integration/enrico.test.ts +++ b/packages/pre/test/enrico.test.ts @@ -1,5 +1,13 @@ // Disabling because we want to access Alice.keyring which is a private property /* eslint-disable @typescript-eslint/no-explicit-any */ +import { + ConditionExpression, + Enrico, + ERC721Ownership, + PolicyMessageKit, + RetrievalResult, + toBytes, +} from '@nucypher/shared'; import { bytesEqual, fakeAlice, @@ -9,15 +17,6 @@ import { } from '@nucypher/test-utils'; import { expect, test } from 'vitest'; -import { - ConditionExpression, - Enrico, - ERC721Ownership, - PolicyMessageKit, - RetrievalResult, - toBytes, -} from '../../src'; - test('enrico', () => { test('alice decrypts message encrypted by enrico', async () => { const label = 'fake-label'; diff --git a/packages/shared/test/integration/message-kit.test.ts b/packages/pre/test/message-kit.test.ts similarity index 87% rename from packages/shared/test/integration/message-kit.test.ts rename to packages/pre/test/message-kit.test.ts index e6445f233..885450920 100644 --- a/packages/shared/test/integration/message-kit.test.ts +++ b/packages/pre/test/message-kit.test.ts @@ -1,8 +1,7 @@ +import { MessageKit, toBytes } from '@nucypher/shared'; import { fakeBob } from '@nucypher/test-utils'; import { expect, test } from 'vitest'; -import { MessageKit, toBytes } from '../../src'; - test('message kit', () => { test('bob decrypts', () => { const bob = fakeBob(); diff --git a/packages/shared/test/unit/pre-strategy.test.ts b/packages/pre/test/pre-strategy.test.ts similarity index 99% rename from packages/shared/test/unit/pre-strategy.test.ts rename to packages/pre/test/pre-strategy.test.ts index 0fb5b6003..f3cc334f3 100644 --- a/packages/shared/test/unit/pre-strategy.test.ts +++ b/packages/pre/test/pre-strategy.test.ts @@ -1,4 +1,14 @@ import { SecretKey, VerifiedKeyFrag } from '@nucypher/nucypher-core'; +import { + ConditionExpression, + DeployedPreStrategy, + ERC721Ownership, + initialize, + PreDecrypter, + PreStrategy, + toBytes, + Ursula, +} from '@nucypher/shared'; import { aliceSecretKeyBytes, bobSecretKeyBytes, @@ -15,17 +25,6 @@ import { } from '@nucypher/test-utils'; import { afterEach, beforeAll, expect, test, vi } from 'vitest'; -import { - ConditionExpression, - DeployedPreStrategy, - ERC721Ownership, - initialize, - PreDecrypter, - PreStrategy, - toBytes, - Ursula, -} from '../../src'; - // Shared test variables const ownsNFT = new ERC721Ownership({ contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', diff --git a/packages/pre/test/pre.test.ts b/packages/pre/test/pre.test.ts index 9fd4423ca..9591b38cb 100644 --- a/packages/pre/test/pre.test.ts +++ b/packages/pre/test/pre.test.ts @@ -1,5 +1,143 @@ -import { expect, test } from 'vitest'; +import { CapsuleFrag, reencrypt } from '@nucypher/nucypher-core'; +import { + Alice, + Bob, + CompoundCondition, + ConditionExpression, + Enrico, + ERC721Ownership, + initialize, + MessageKit, + PolicyMessageKit, + RetrievalResult, + toBytes, + zip, +} from '@nucypher/shared'; +import { + fakeAlice, + fakeBob, + fakeUrsulas, + reencryptKFrags, +} from '@nucypher/test-utils'; +import { beforeAll, expect, test } from 'vitest'; -test('pre', () => { - expect('pre').toBe('pre'); +test('proxy reencryption', () => { + let alice: Alice; + let bob: Bob; + const plaintext = toBytes('plaintext-message'); + const threshold = 2; + const shares = 3; + const label = 'fake-data-label'; + + test('verifies capsule frags', async () => { + beforeAll(async () => { + await initialize(); + bob = fakeBob(); + alice = fakeAlice(); + }); + + const { capsule } = new MessageKit(bob.decryptingKey, plaintext, null); + const { delegatingKey, verifiedKFrags } = alice.generateKFrags( + bob, + label, + threshold, + shares, + ); + + const { verifiedCFrags } = reencryptKFrags(verifiedKFrags, capsule); + const cFrags = verifiedCFrags.map((verifiedCFrag) => + CapsuleFrag.fromBytes(verifiedCFrag.toBytes()), + ); + const areVerified = cFrags.every((cFrag) => + cFrag.verify( + capsule, + alice.verifyingKey, + delegatingKey, + bob.decryptingKey, + ), + ); + expect(areVerified).toBeTruthy(); + }); + + test('encrypts and decrypts reencrypted message', async () => { + const { verifiedKFrags } = alice.generateKFrags( + bob, + label, + threshold, + shares, + ); + + const policyEncryptingKey = alice.getPolicyEncryptingKeyFromLabel(label); + const enrico = new Enrico(policyEncryptingKey); + const encryptedMessage = enrico.encryptMessagePre(plaintext); + + const ursulaAddresses = fakeUrsulas().map( + (ursula) => ursula.checksumAddress, + ); + const reencrypted = verifiedKFrags.map((kFrag) => + reencrypt(encryptedMessage.capsule, kFrag), + ); + const results = new RetrievalResult( + Object.fromEntries(zip(ursulaAddresses, reencrypted)), + ); + const policyMessageKit = PolicyMessageKit.fromMessageKit( + encryptedMessage, + policyEncryptingKey, + threshold, + ).withResult(results); + expect(policyMessageKit.isDecryptableByReceiver()).toBeTruthy(); + + const bobPlaintext = bob.decrypt(policyMessageKit); + expect(bobPlaintext).toEqual(plaintext); + }); + + test('encrypts and decrypts reencrypted message with conditions', async () => { + const { verifiedKFrags } = alice.generateKFrags( + bob, + label, + threshold, + shares, + ); + + const policyEncryptingKey = alice.getPolicyEncryptingKeyFromLabel(label); + + const genuineUndead = new ERC721Ownership({ + contractAddress: '0x209e639a0EC166Ac7a1A4bA41968fa967dB30221', + chain: 1, + parameters: [1], + }); + const gnomePals = new ERC721Ownership({ + contractAddress: '0x5dB11d7356aa4C0E85Aa5b255eC2B5F81De6d4dA', + chain: 1, + parameters: [1], + }); + const conditionsSet = new ConditionExpression( + new CompoundCondition({ + operator: 'or', + operands: [genuineUndead.toObj(), gnomePals.toObj()], + }), + ); + + const enrico = new Enrico(policyEncryptingKey, undefined, conditionsSet); + const encryptedMessage = enrico.encryptMessagePre(plaintext); + + const ursulaAddresses = fakeUrsulas().map( + (ursula) => ursula.checksumAddress, + ); + const reencrypted = verifiedKFrags.map((kFrag) => + reencrypt(encryptedMessage.capsule, kFrag), + ); + const results = new RetrievalResult( + Object.fromEntries(zip(ursulaAddresses, reencrypted)), + ); + const policyMessageKit = PolicyMessageKit.fromMessageKit( + encryptedMessage, + policyEncryptingKey, + threshold, + ).withResult(results); + expect(policyMessageKit.isDecryptableByReceiver()).toBeTruthy(); + + const bobPlaintext = bob.decrypt(policyMessageKit); + expect(bobPlaintext).toEqual(plaintext); + }); }); diff --git a/packages/pre/test/test-utils.ts b/packages/pre/test/test-utils.ts new file mode 100644 index 000000000..c21d428e5 --- /dev/null +++ b/packages/pre/test/test-utils.ts @@ -0,0 +1,81 @@ +// Disabling some of the eslint rules for convenience. +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unused-vars */ + +import { + Capsule, + EncryptedTreasureMap, + PublicKey, + reencrypt, + SecretKey, + VerifiedCapsuleFrag, + VerifiedKeyFrag, +} from '@nucypher/nucypher-core'; +import { toBytes } from '@nucypher/shared'; +import { SpyInstance, vi } from 'vitest'; + +import { Alice, Bob, RemoteBob } from '../src/characters'; +import { BlockchainPolicy, PreEnactedPolicy } from '../src/policy'; + +export const fakeBob = (): Bob => { + const secretKey = SecretKey.fromBEBytes( + toBytes('fake-secret-key-32-bytes-bob-xxx'), + ); + return Bob.fromSecretKey(secretKey); +}; + +export const fakeRemoteBob = (): RemoteBob => { + const { decryptingKey, verifyingKey } = fakeBob(); + return RemoteBob.fromKeys(decryptingKey, verifyingKey); +}; + +export const fakeAlice = (aliceKey = 'fake-secret-key-32-bytes-alice-x') => { + const secretKey = SecretKey.fromBEBytes(toBytes(aliceKey)); + return Alice.fromSecretKey(secretKey); +}; + +export const mockPublishToBlockchain = (): SpyInstance => { + const txHash = '0x1234567890123456789012345678901234567890'; + return vi + .spyOn(PreEnactedPolicy.prototype as any, 'publish') + .mockImplementation(async () => Promise.resolve(txHash)); +}; + +export const mockGenerateKFrags = (withValue?: { + delegatingKey: PublicKey; + verifiedKFrags: VerifiedKeyFrag[]; +}): SpyInstance => { + const spy = vi.spyOn(Alice.prototype as any, 'generateKFrags'); + if (withValue) { + return spy.mockImplementation(() => withValue); + } + return spy; +}; + +export const mockEncryptTreasureMap = ( + withValue?: EncryptedTreasureMap, +): SpyInstance => { + const spy = vi.spyOn(BlockchainPolicy.prototype as any, 'encryptTreasureMap'); + if (withValue) { + return spy.mockImplementation(() => withValue); + } + return spy; +}; + +export const reencryptKFrags = ( + kFrags: readonly VerifiedKeyFrag[], + capsule: Capsule, +): { + verifiedCFrags: VerifiedCapsuleFrag[]; +} => { + if (!kFrags) { + throw new Error('Pass at least one kFrag.'); + } + const verifiedCFrags = kFrags.map((kFrag) => reencrypt(capsule, kFrag)); + return { verifiedCFrags }; +}; + +export const mockMakeTreasureMap = (): SpyInstance => { + return vi.spyOn(BlockchainPolicy.prototype as any, 'makeTreasureMap'); +}; diff --git a/packages/shared/src/characters/enrico.ts b/packages/shared/src/characters/enrico.ts deleted file mode 100644 index 470e296f8..000000000 --- a/packages/shared/src/characters/enrico.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { - AccessControlPolicy, - DkgPublicKey, - encryptForDkg, - MessageKit, - PublicKey, - SecretKey, - ThresholdMessageKit, -} from '@nucypher/nucypher-core'; -import { arrayify, keccak256 } from 'ethers/lib/utils'; - -import { ConditionExpression } from '../conditions'; -import { Keyring } from '../keyring'; -import { toBytes } from '../utils'; - -export class Enrico { - public readonly encryptingKey: PublicKey | DkgPublicKey; - private readonly keyring: Keyring; - public conditions?: ConditionExpression | undefined; - - constructor( - encryptingKey: PublicKey | DkgPublicKey, - verifyingKey?: SecretKey, - conditions?: ConditionExpression, - ) { - this.encryptingKey = encryptingKey; - this.keyring = new Keyring(verifyingKey ?? SecretKey.random()); - this.conditions = conditions; - } - - public get verifyingKey(): PublicKey { - return this.keyring.publicKey; - } - - public encryptMessagePre( - plaintext: Uint8Array | string, - withConditions?: ConditionExpression, - ): MessageKit { - if (!withConditions) { - withConditions = this.conditions; - } - - if (!(this.encryptingKey instanceof PublicKey)) { - throw new Error('Wrong key type. Use encryptMessageCbd instead.'); - } - - return new MessageKit( - this.encryptingKey, - plaintext instanceof Uint8Array ? plaintext : toBytes(plaintext), - withConditions ? withConditions.toWASMConditions() : null, - ); - } - - public encryptMessageCbd( - plaintext: Uint8Array | string, - conditions?: ConditionExpression, - ): ThresholdMessageKit { - if (!conditions) { - conditions = this.conditions; - } - - if (!conditions) { - throw new Error('Conditions are required for CBD encryption.'); - } - - if (!(this.encryptingKey instanceof DkgPublicKey)) { - throw new Error('Wrong key type. Use encryptMessagePre instead.'); - } - - const [ciphertext, authenticatedData] = encryptForDkg( - plaintext instanceof Uint8Array ? plaintext : toBytes(plaintext), - this.encryptingKey, - conditions.toWASMConditions(), - ); - - const headerHash = keccak256(ciphertext.header.toBytes()); - const authorization = this.keyring.signer.sign(arrayify(headerHash)); - const acp = new AccessControlPolicy( - authenticatedData, - authorization.toBEBytes(), - ); - - return new ThresholdMessageKit(ciphertext, acp); - } -} diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 4a872749a..08b6bfa4c 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1,11 +1,6 @@ -export * from './characters'; export * from './cohort'; export * from './conditions'; export * from './contracts'; -export * from './dkg'; -export * from './keyring'; -export * from './kits'; -export * from './policy'; export * from './porter'; export * from './types'; export * from './utils'; diff --git a/packages/shared/test/unit/cohort.test.ts b/packages/shared/test/cohort.test.ts similarity index 95% rename from packages/shared/test/unit/cohort.test.ts rename to packages/shared/test/cohort.test.ts index 31372a7a3..8be3f0e7b 100644 --- a/packages/shared/test/unit/cohort.test.ts +++ b/packages/shared/test/cohort.test.ts @@ -1,7 +1,7 @@ import { fakeUrsulas, makeCohort } from '@nucypher/test-utils'; import { beforeAll, expect, test } from 'vitest'; -import { Cohort, initialize } from '../../src'; +import { Cohort, initialize } from '../src'; test('Cohort', () => { beforeAll(async () => { diff --git a/packages/shared/test/unit/conditions/base/condition.test.ts b/packages/shared/test/conditions/base/condition.test.ts similarity index 86% rename from packages/shared/test/unit/conditions/base/condition.test.ts rename to packages/shared/test/conditions/base/condition.test.ts index 4d4c03a51..410f990f5 100644 --- a/packages/shared/test/unit/conditions/base/condition.test.ts +++ b/packages/shared/test/conditions/base/condition.test.ts @@ -5,12 +5,12 @@ import { } from '@nucypher/test-utils'; import { expect, test } from 'vitest'; -import { Condition } from '../../../../src'; -import { ContractCondition } from '../../../../src/conditions/base'; +import { Condition } from '../../../src'; +import { ContractCondition } from '../../../src/conditions/base'; import { ERC721Balance, ERC721Ownership, -} from '../../../../src/conditions/predefined'; +} from '../../../src/conditions/predefined'; test('validation', () => { const condition = new ERC721Balance({ diff --git a/packages/shared/test/unit/conditions/base/contract.test.ts b/packages/shared/test/conditions/base/contract.test.ts similarity index 98% rename from packages/shared/test/unit/conditions/base/contract.test.ts rename to packages/shared/test/conditions/base/contract.test.ts index ce9449d4c..3037fbdb5 100644 --- a/packages/shared/test/unit/conditions/base/contract.test.ts +++ b/packages/shared/test/conditions/base/contract.test.ts @@ -12,12 +12,12 @@ import { ContractConditionProps, CustomContextParam, initialize, -} from '../../../../src'; +} from '../../../src'; import { contractConditionSchema, FunctionAbiProps, -} from '../../../../src/conditions/base/contract'; -import { USER_ADDRESS_PARAM } from '../../../../src/conditions/const'; +} from '../../../src/conditions/base/contract'; +import { USER_ADDRESS_PARAM } from '../../../src/conditions/const'; test('validation', () => { test('accepts on a valid schema', () => { diff --git a/packages/shared/test/unit/conditions/base/rpc.test.ts b/packages/shared/test/conditions/base/rpc.test.ts similarity index 87% rename from packages/shared/test/unit/conditions/base/rpc.test.ts rename to packages/shared/test/conditions/base/rpc.test.ts index 55b1256f9..548cd2476 100644 --- a/packages/shared/test/unit/conditions/base/rpc.test.ts +++ b/packages/shared/test/conditions/base/rpc.test.ts @@ -1,8 +1,8 @@ import { testRpcConditionObj } from '@nucypher/test-utils'; import { expect, test } from 'vitest'; -import { RpcCondition } from '../../../../src/conditions/base'; -import { rpcConditionSchema } from '../../../../src/conditions/base/rpc'; +import { RpcCondition } from '../../../src/conditions/base'; +import { rpcConditionSchema } from '../../../src/conditions/base/rpc'; test('validation', () => { test('accepts on a valid schema', () => { diff --git a/packages/shared/test/unit/conditions/base/time.test.ts b/packages/shared/test/conditions/base/time.test.ts similarity index 89% rename from packages/shared/test/unit/conditions/base/time.test.ts rename to packages/shared/test/conditions/base/time.test.ts index 2b08a9c34..7e7984c56 100644 --- a/packages/shared/test/unit/conditions/base/time.test.ts +++ b/packages/shared/test/conditions/base/time.test.ts @@ -3,13 +3,13 @@ import { expect, test } from 'vitest'; import { TimeCondition, TimeConditionProps, -} from '../../../../src/conditions/base'; -import { ReturnValueTestProps } from '../../../../src/conditions/base/shared'; +} from '../../../src/conditions/base'; +import { ReturnValueTestProps } from '../../../src/conditions/base/shared'; import { TimeConditionMethod, timeConditionSchema, TimeConditionType, -} from '../../../../src/conditions/base/time'; +} from '../../../src/conditions/base/time'; test('validation', () => { const returnValueTest: ReturnValueTestProps = { diff --git a/packages/shared/test/unit/conditions/compound-condition.test.ts b/packages/shared/test/conditions/compound-condition.test.ts similarity index 95% rename from packages/shared/test/unit/conditions/compound-condition.test.ts rename to packages/shared/test/conditions/compound-condition.test.ts index b2313591d..0370735d9 100644 --- a/packages/shared/test/unit/conditions/compound-condition.test.ts +++ b/packages/shared/test/conditions/compound-condition.test.ts @@ -5,12 +5,12 @@ import { } from '@nucypher/test-utils'; import { expect, test } from 'vitest'; -import { Condition } from '../../../src'; -import { CompoundCondition } from '../../../src/conditions/base'; +import { Condition } from '../../src'; +import { CompoundCondition } from '../../src/conditions/base'; import { compoundConditionSchema, CompoundConditionType, -} from '../../../src/conditions/compound-condition'; +} from '../../src/conditions/compound-condition'; test('validation', () => { test('accepts or operator', () => { diff --git a/packages/shared/test/unit/conditions/condition-expr.test.ts b/packages/shared/test/conditions/condition-expr.test.ts similarity index 98% rename from packages/shared/test/unit/conditions/condition-expr.test.ts rename to packages/shared/test/conditions/condition-expr.test.ts index fba45abbf..f9316a382 100644 --- a/packages/shared/test/unit/conditions/condition-expr.test.ts +++ b/packages/shared/test/conditions/condition-expr.test.ts @@ -10,7 +10,7 @@ import { import { SemVer } from 'semver'; import { expect, test } from 'vitest'; -import { ConditionExpression, objectEquals, toJSON } from '../../../src'; +import { ConditionExpression, objectEquals, toJSON } from '../../src'; import { CompoundCondition, ContractCondition, @@ -19,9 +19,9 @@ import { RpcConditionType, TimeCondition, TimeConditionProps, -} from '../../../src/conditions/base'; -import { USER_ADDRESS_PARAM } from '../../../src/conditions/const'; -import { ERC721Balance } from '../../../src/conditions/predefined'; +} from '../../src/conditions/base'; +import { USER_ADDRESS_PARAM } from '../../src/conditions/const'; +import { ERC721Balance } from '../../src/conditions/predefined'; test('condition set', () => { const erc721BalanceCondition = new ERC721Balance({ diff --git a/packages/shared/test/unit/conditions/context.test.ts b/packages/shared/test/conditions/context.test.ts similarity index 97% rename from packages/shared/test/unit/conditions/context.test.ts rename to packages/shared/test/conditions/context.test.ts index 8ff23e591..53352e874 100644 --- a/packages/shared/test/unit/conditions/context.test.ts +++ b/packages/shared/test/conditions/context.test.ts @@ -15,9 +15,9 @@ import { CustomContextParam, initialize, RpcCondition, -} from '../../../src'; -import { USER_ADDRESS_PARAM } from '../../../src/conditions/const'; -import { RESERVED_CONTEXT_PARAMS } from '../../../src/conditions/context/context'; +} from '../../src'; +import { USER_ADDRESS_PARAM } from '../../src/conditions/const'; +import { RESERVED_CONTEXT_PARAMS } from '../../src/conditions/context/context'; test('context', () => { let provider: ethers.providers.Provider; diff --git a/packages/shared/test/integration/pre.test.ts b/packages/shared/test/integration/pre.test.ts deleted file mode 100644 index c64dddbec..000000000 --- a/packages/shared/test/integration/pre.test.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { CapsuleFrag, reencrypt } from '@nucypher/nucypher-core'; -import { - fakeAlice, - fakeBob, - fakeUrsulas, - reencryptKFrags, -} from '@nucypher/test-utils'; -import { beforeAll, expect, test } from 'vitest'; - -import { - Alice, - Bob, - CompoundCondition, - ConditionExpression, - Enrico, - ERC721Ownership, - initialize, - MessageKit, - PolicyMessageKit, - RetrievalResult, - toBytes, - zip, -} from '../../src'; - -test('proxy reencryption', () => { - let alice: Alice; - let bob: Bob; - const plaintext = toBytes('plaintext-message'); - const threshold = 2; - const shares = 3; - const label = 'fake-data-label'; - - test('verifies capsule frags', async () => { - beforeAll(async () => { - await initialize(); - bob = fakeBob(); - alice = fakeAlice(); - }); - - const { capsule } = new MessageKit(bob.decryptingKey, plaintext, null); - const { delegatingKey, verifiedKFrags } = alice.generateKFrags( - bob, - label, - threshold, - shares, - ); - - const { verifiedCFrags } = reencryptKFrags(verifiedKFrags, capsule); - const cFrags = verifiedCFrags.map((verifiedCFrag) => - CapsuleFrag.fromBytes(verifiedCFrag.toBytes()), - ); - const areVerified = cFrags.every((cFrag) => - cFrag.verify( - capsule, - alice.verifyingKey, - delegatingKey, - bob.decryptingKey, - ), - ); - expect(areVerified).toBeTruthy(); - }); - - test('encrypts and decrypts reencrypted message', async () => { - const { verifiedKFrags } = alice.generateKFrags( - bob, - label, - threshold, - shares, - ); - - const policyEncryptingKey = alice.getPolicyEncryptingKeyFromLabel(label); - const enrico = new Enrico(policyEncryptingKey); - const encryptedMessage = enrico.encryptMessagePre(plaintext); - - const ursulaAddresses = fakeUrsulas().map( - (ursula) => ursula.checksumAddress, - ); - const reencrypted = verifiedKFrags.map((kFrag) => - reencrypt(encryptedMessage.capsule, kFrag), - ); - const results = new RetrievalResult( - Object.fromEntries(zip(ursulaAddresses, reencrypted)), - ); - const policyMessageKit = PolicyMessageKit.fromMessageKit( - encryptedMessage, - policyEncryptingKey, - threshold, - ).withResult(results); - expect(policyMessageKit.isDecryptableByReceiver()).toBeTruthy(); - - const bobPlaintext = bob.decrypt(policyMessageKit); - expect(bobPlaintext).toEqual(plaintext); - }); - - test('encrypts and decrypts reencrypted message with conditions', async () => { - const { verifiedKFrags } = alice.generateKFrags( - bob, - label, - threshold, - shares, - ); - - const policyEncryptingKey = alice.getPolicyEncryptingKeyFromLabel(label); - - const genuineUndead = new ERC721Ownership({ - contractAddress: '0x209e639a0EC166Ac7a1A4bA41968fa967dB30221', - chain: 1, - parameters: [1], - }); - const gnomePals = new ERC721Ownership({ - contractAddress: '0x5dB11d7356aa4C0E85Aa5b255eC2B5F81De6d4dA', - chain: 1, - parameters: [1], - }); - const conditionsSet = new ConditionExpression( - new CompoundCondition({ - operator: 'or', - operands: [genuineUndead.toObj(), gnomePals.toObj()], - }), - ); - - const enrico = new Enrico(policyEncryptingKey, undefined, conditionsSet); - const encryptedMessage = enrico.encryptMessagePre(plaintext); - - const ursulaAddresses = fakeUrsulas().map( - (ursula) => ursula.checksumAddress, - ); - const reencrypted = verifiedKFrags.map((kFrag) => - reencrypt(encryptedMessage.capsule, kFrag), - ); - const results = new RetrievalResult( - Object.fromEntries(zip(ursulaAddresses, reencrypted)), - ); - const policyMessageKit = PolicyMessageKit.fromMessageKit( - encryptedMessage, - policyEncryptingKey, - threshold, - ).withResult(results); - expect(policyMessageKit.isDecryptableByReceiver()).toBeTruthy(); - - const bobPlaintext = bob.decrypt(policyMessageKit); - expect(bobPlaintext).toEqual(plaintext); - }); -}); diff --git a/packages/shared/src/dkg.ts b/packages/taco/src/dkg.ts similarity index 96% rename from packages/shared/src/dkg.ts rename to packages/taco/src/dkg.ts index 85ef98876..6ee67d216 100644 --- a/packages/shared/src/dkg.ts +++ b/packages/taco/src/dkg.ts @@ -1,10 +1,12 @@ import { DkgPublicKey } from '@nucypher/nucypher-core'; +import { + ChecksumAddress, + DkgCoordinatorAgent, + DkgRitualState, + fromHexString, +} from '@nucypher/shared'; import { BigNumberish, ethers } from 'ethers'; -import { DkgCoordinatorAgent, DkgRitualState } from './contracts'; -import { ChecksumAddress } from './types'; -import { fromHexString } from './utils'; - export interface DkgRitualJSON { id: number; dkgPublicKey: Uint8Array; diff --git a/packages/taco/src/taco.ts b/packages/taco/src/taco.ts index 0eb6564de..2208928b8 100644 --- a/packages/taco/src/taco.ts +++ b/packages/taco/src/taco.ts @@ -9,7 +9,6 @@ import { import { Condition, ConditionExpression, - DkgClient, DkgCoordinatorAgent, getPorterUri, toBytes, @@ -17,6 +16,7 @@ import { import { ethers } from 'ethers'; import { arrayify, keccak256 } from 'ethers/lib/utils'; +import { DkgClient } from './dkg'; import { retrieveAndDecrypt } from './tdec'; export const encrypt = async ( @@ -45,11 +45,11 @@ export const encryptWithPublicKey = async ( message: string, condition: Condition, dkgPublicKey: DkgPublicKey, - authorizationSigner?: Signer, + authSigner?: Signer, ): Promise => { const conditionExpr = new ConditionExpression(condition); - if (!authorizationSigner) { - authorizationSigner = new Signer(SecretKey.random()); + if (!authSigner) { + authSigner = new Signer(SecretKey.random()); } const [ciphertext, authenticatedData] = encryptForDkg( @@ -59,7 +59,7 @@ export const encryptWithPublicKey = async ( ); const headerHash = keccak256(ciphertext.header.toBytes()); - const authorization = authorizationSigner.sign(arrayify(headerHash)); + const authorization = authSigner.sign(arrayify(headerHash)); const acp = new AccessControlPolicy( authenticatedData, authorization.toBEBytes(), diff --git a/packages/shared/test/unit/cbd-strategy.test.ts b/packages/taco/test/cbd-strategy.test.ts similarity index 99% rename from packages/shared/test/unit/cbd-strategy.test.ts rename to packages/taco/test/cbd-strategy.test.ts index 951ae91a9..e17cf11c6 100644 --- a/packages/shared/test/unit/cbd-strategy.test.ts +++ b/packages/taco/test/cbd-strategy.test.ts @@ -3,6 +3,15 @@ import { SecretKey, SessionStaticSecret, } from '@nucypher/nucypher-core'; +import { + CbdStrategy, + ConditionExpression, + DeployedCbdStrategy, + ERC721Ownership, + initialize, + ThresholdDecrypter, + toBytes, +} from '@nucypher/shared'; import { aliceSecretKeyBytes, fakeDkgFlow, @@ -22,16 +31,6 @@ import { import { ethers } from 'ethers'; import { afterEach, beforeAll, expect, test, vi } from 'vitest'; -import { - CbdStrategy, - ConditionExpression, - DeployedCbdStrategy, - ERC721Ownership, - initialize, - ThresholdDecrypter, - toBytes, -} from '../../src'; - // Shared test variables const ownsNFT = new ERC721Ownership({ contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', diff --git a/packages/shared/test/integration/dkg-client.test.ts b/packages/taco/test/dkg-client.test.ts similarity index 96% rename from packages/shared/test/integration/dkg-client.test.ts rename to packages/taco/test/dkg-client.test.ts index bc86815bd..ba0454c45 100644 --- a/packages/shared/test/integration/dkg-client.test.ts +++ b/packages/taco/test/dkg-client.test.ts @@ -1,3 +1,4 @@ +import { DkgCoordinatorAgent, SecretKey } from '@nucypher/shared'; import { fakeProvider, mockCoordinatorRitual, @@ -7,8 +8,6 @@ import { } from '@nucypher/test-utils'; import { afterEach, expect, test, vi } from 'vitest'; -import { DkgCoordinatorAgent, SecretKey } from '../../src'; - vi.mock('../../src/contracts/agents/coordinator', () => ({ DkgCoordinatorAgent: { getRitual: () => Promise.resolve(mockCoordinatorRitual(mockRitualId)), diff --git a/packages/shared/test/unit/ritual.test.ts b/packages/taco/test/ritual.test.ts similarity index 92% rename from packages/shared/test/unit/ritual.test.ts rename to packages/taco/test/ritual.test.ts index d4645920a..572aa657c 100644 --- a/packages/shared/test/unit/ritual.test.ts +++ b/packages/taco/test/ritual.test.ts @@ -1,8 +1,7 @@ import { DkgPublicKey } from '@nucypher/nucypher-core'; +import { fromHexString } from '@nucypher/shared'; import { expect, test } from 'vitest'; -import { fromHexString } from '../../src'; - test('Ritual', () => { test('deserializes pre-made dkg ritual', async () => { const pkWord1 = fromHexString( diff --git a/packages/taco/test/test-utils.ts b/packages/taco/test/test-utils.ts index 1446f5a29..563ff804c 100644 --- a/packages/taco/test/test-utils.ts +++ b/packages/taco/test/test-utils.ts @@ -3,15 +3,181 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { SessionStaticSecret } from '@nucypher/nucypher-core'; +import { + AggregatedTranscript, + DecryptionShareSimple, + Dkg, + FerveoVariant, + Keypair, + SessionSecretFactory, + SessionStaticSecret, + ThresholdMessageKit, + Transcript, + Validator, + ValidatorMessage, +} from '@nucypher/nucypher-core'; +import { + ConditionExpression, + DkgParticipant, + DkgRitualState, + toBytes, + toHexString, + zip, +} from '@nucypher/shared'; +import { + fakeConditionExpr, + fakeDkgFlow, + fakeTDecFlow, +} from '@nucypher/test-utils'; +import { keccak256 } from 'ethers/lib/utils'; import { SpyInstance, vi } from 'vitest'; -import { ThresholdDecrypter } from '../src'; +import { DkgClient, DkgRitual } from '../src/dkg'; +import { encryptMessageCbd } from '../src/tdec'; -export const mockRandomSessionStaticSecret = ( - secret: SessionStaticSecret, +export const fakeDkgTDecFlowE2E: ( + ritualId?: number, + variant?: FerveoVariant, + conditionExpr?: ConditionExpression, + message?: Uint8Array, + sharesNum?: number, + threshold?: number, +) => { + dkg: Dkg; + serverAggregate: AggregatedTranscript; + sharesNum: number; + transcripts: Transcript[]; + validatorKeypairs: Keypair[]; + validators: Validator[]; + ritualId: number; + threshold: number; + receivedMessages: ValidatorMessage[]; + message: Uint8Array; + thresholdMessageKit: ThresholdMessageKit; + decryptionShares: DecryptionShareSimple[]; +} = ( + ritualId = 0, + variant: FerveoVariant = FerveoVariant.precomputed, + conditionExpr: ConditionExpression = fakeConditionExpr(), + message = toBytes('fake-message'), + sharesNum = 4, + threshold = 4, +) => { + const ritual = fakeDkgFlow(variant, ritualId, sharesNum, threshold); + const dkgPublicKey = ritual.dkg.publicKey(); + const thresholdMessageKit = encryptMessageCbd( + message, + dkgPublicKey, + conditionExpr, + ); + + const { decryptionShares } = fakeTDecFlow({ + ...ritual, + message, + dkgPublicKey, + thresholdMessageKit, + }); + + return { + ...ritual, + message, + decryptionShares, + thresholdMessageKit, + }; +}; + +export const mockCoordinatorRitual = async ( + ritualId: number, +): Promise<{ + aggregationMismatch: boolean; + initTimestamp: number; + aggregatedTranscriptHash: string; + initiator: string; + dkgSize: number; + id: number; + publicKey: { word1: string; word0: string }; + totalTranscripts: number; + aggregatedTranscript: string; + publicKeyHash: string; + totalAggregations: number; +}> => { + const ritual = await fakeDkgTDecFlowE2E(); + const dkgPkBytes = ritual.dkg.publicKey().toBytes(); + return { + id: ritualId, + initiator: ritual.validators[0].address.toString(), + dkgSize: ritual.sharesNum, + initTimestamp: 0, + totalTranscripts: ritual.receivedMessages.length, + totalAggregations: ritual.sharesNum, // Assuming the ritual is finished + aggregatedTranscriptHash: keccak256(ritual.serverAggregate.toBytes()), + aggregationMismatch: false, // Assuming the ritual is correct + aggregatedTranscript: toHexString(ritual.serverAggregate.toBytes()), + publicKey: { + word0: toHexString(dkgPkBytes.slice(0, 32)), + word1: toHexString(dkgPkBytes.slice(32, 48)), + }, + publicKeyHash: keccak256(ritual.dkg.publicKey().toBytes()), + }; +}; + +export const mockDkgParticipants = ( + ritualId: number, +): { + participants: DkgParticipant[]; + participantSecrets: Record; +} => { + const ritual = fakeDkgTDecFlowE2E(ritualId); + const label = toBytes(`${ritualId}`); + + const participantSecrets: Record = + Object.fromEntries( + ritual.validators.map(({ address }) => { + const participantSecret = SessionSecretFactory.random().makeKey(label); + return [address.toString(), participantSecret]; + }), + ); + + const participants: DkgParticipant[] = zip( + Object.entries(participantSecrets), + ritual.transcripts, + ).map(([[address, secret], transcript]) => { + return { + provider: address, + aggregated: true, // Assuming all validators already contributed to the aggregate + transcript, + decryptionRequestStaticKey: secret.publicKey(), + } as DkgParticipant; + }); + return { participantSecrets, participants }; +}; + +export const mockRitualId = 0; + +export const fakeDkgRitual = (ritual: { + dkg: Dkg; + sharesNum: number; + threshold: number; +}) => { + return new DkgRitual( + mockRitualId, + ritual.dkg.publicKey(), + ritual.sharesNum, + ritual.threshold, + DkgRitualState.FINALIZED, + ); +}; + +export const mockGetRitual = (dkgRitual: DkgRitual): SpyInstance => { + return vi.spyOn(DkgClient, 'getRitual').mockImplementation(() => { + return Promise.resolve(dkgRitual); + }); +}; + +export const mockGetFinalizedRitualSpy = ( + dkgRitual: DkgRitual, ): SpyInstance => { - return vi - .spyOn(ThresholdDecrypter.prototype as any, 'makeSessionKey') - .mockImplementation(() => secret); + return vi.spyOn(DkgClient, 'getFinalizedRitual').mockImplementation(() => { + return Promise.resolve(dkgRitual); + }); }; diff --git a/packages/test-utils/src/utils.ts b/packages/test-utils/src/utils.ts index 3d79a3a4a..48a919165 100644 --- a/packages/test-utils/src/utils.ts +++ b/packages/test-utils/src/utils.ts @@ -12,14 +12,11 @@ import { Dkg, DkgPublicKey, EncryptedThresholdDecryptionResponse, - EncryptedTreasureMap, EthereumAddress, FerveoVariant, Keypair, - PublicKey, reencrypt, SecretKey, - SessionSecretFactory, SessionStaticKey, SessionStaticSecret, ThresholdDecryptionResponse, @@ -27,30 +24,19 @@ import { Transcript, Validator, ValidatorMessage, - VerifiedCapsuleFrag, VerifiedKeyFrag, } from '@nucypher/nucypher-core'; import { - Alice, - BlockchainPolicy, - Bob, CbdDecryptResult, ChecksumAddress, Cohort, ConditionExpression, - DkgClient, DkgCoordinatorAgent, DkgParticipant, - DkgRitual, - DkgRitualState, - Enrico, ERC721Balance, GetUrsulasResult, PorterClient, - PreEnactedPolicy, - RemoteBob, RetrieveCFragsResult, - toBytes, toHexString, Ursula, zip, @@ -58,7 +44,6 @@ import { import { MessageKit } from '@nucypher/shared/dist/es'; import axios from 'axios'; import { ethers, providers, Wallet } from 'ethers'; -import { keccak256 } from 'ethers/lib/utils'; import { expect, SpyInstance, vi } from 'vitest'; import { TEST_CHAIN_ID, TEST_CONTRACT_ADDR } from './variables'; @@ -72,23 +57,6 @@ export const fromBytes = (bytes: Uint8Array): string => export const fakePorterUri = 'https://_this_should_crash.com/'; -export const fakeBob = (): Bob => { - const secretKey = SecretKey.fromBEBytes( - toBytes('fake-secret-key-32-bytes-bob-xxx'), - ); - return Bob.fromSecretKey(secretKey); -}; - -export const fakeRemoteBob = (): RemoteBob => { - const { decryptingKey, verifyingKey } = fakeBob(); - return RemoteBob.fromKeys(decryptingKey, verifyingKey); -}; - -export const fakeAlice = (aliceKey = 'fake-secret-key-32-bytes-alice-x') => { - const secretKey = SecretKey.fromBEBytes(toBytes(aliceKey)); - return Alice.fromSecretKey(secretKey); -}; - const makeFakeProvider = (timestamp: number, blockNumber: number) => { const block = { timestamp }; return { @@ -166,12 +134,6 @@ export const mockGetUrsulas = ( return Promise.resolve({ data: fakePorterUrsulas(ursulas) }); }); }; -export const mockPublishToBlockchain = (): SpyInstance => { - const txHash = '0x1234567890123456789012345678901234567890'; - return vi - .spyOn(PreEnactedPolicy.prototype as any, 'publish') - .mockImplementation(async () => Promise.resolve(txHash)); -}; const fakeCFragResponse = ( ursulas: readonly ChecksumAddress[], @@ -197,43 +159,6 @@ export const mockRetrieveCFragsRequest = ( return Promise.resolve(results); }); }; -export const mockGenerateKFrags = (withValue?: { - delegatingKey: PublicKey; - verifiedKFrags: VerifiedKeyFrag[]; -}): SpyInstance => { - const spy = vi.spyOn(Alice.prototype as any, 'generateKFrags'); - if (withValue) { - return spy.mockImplementation(() => withValue); - } - return spy; -}; - -export const mockEncryptTreasureMap = ( - withValue?: EncryptedTreasureMap, -): SpyInstance => { - const spy = vi.spyOn(BlockchainPolicy.prototype as any, 'encryptTreasureMap'); - if (withValue) { - return spy.mockImplementation(() => withValue); - } - return spy; -}; - -export const reencryptKFrags = ( - kFrags: readonly VerifiedKeyFrag[], - capsule: Capsule, -): { - verifiedCFrags: VerifiedCapsuleFrag[]; -} => { - if (!kFrags) { - throw new Error('Pass at least one kFrag.'); - } - const verifiedCFrags = kFrags.map((kFrag) => reencrypt(capsule, kFrag)); - return { verifiedCFrags }; -}; - -export const mockMakeTreasureMap = (): SpyInstance => { - return vi.spyOn(BlockchainPolicy.prototype as any, 'makeTreasureMap'); -}; export const mockDetectEthereumProvider = (): (() => providers.ExternalProvider) => { @@ -361,7 +286,7 @@ export const fakeTDecFlow = ({ }; }; -const fakeConditionExpr = () => { +export const fakeConditionExpr = () => { const erc721Balance = new ERC721Balance({ chain: TEST_CHAIN_ID, contractAddress: TEST_CONTRACT_ADDR, @@ -369,122 +294,6 @@ const fakeConditionExpr = () => { return new ConditionExpression(erc721Balance); }; -export const fakeDkgTDecFlowE2E: ( - ritualId?: number, - variant?: FerveoVariant, - conditionExpr?: ConditionExpression, - message?: Uint8Array, - sharesNum?: number, - threshold?: number, -) => { - dkg: Dkg; - serverAggregate: AggregatedTranscript; - sharesNum: number; - transcripts: Transcript[]; - validatorKeypairs: Keypair[]; - validators: Validator[]; - ritualId: number; - threshold: number; - receivedMessages: ValidatorMessage[]; - message: Uint8Array; - thresholdMessageKit: ThresholdMessageKit; - decryptionShares: DecryptionShareSimple[]; -} = ( - ritualId = 0, - variant: FerveoVariant = FerveoVariant.precomputed, - conditionExpr: ConditionExpression = fakeConditionExpr(), - message = toBytes('fake-message'), - sharesNum = 4, - threshold = 4, -) => { - const ritual = fakeDkgFlow(variant, ritualId, sharesNum, threshold); - const dkgPublicKey = ritual.dkg.publicKey(); - const thresholdMessageKit = new Enrico(dkgPublicKey).encryptMessageCbd( - message, - conditionExpr, - ); - - const { decryptionShares } = fakeTDecFlow({ - ...ritual, - message, - dkgPublicKey, - thresholdMessageKit, - }); - - return { - ...ritual, - message, - decryptionShares, - thresholdMessageKit, - }; -}; - -export const mockCoordinatorRitual = async ( - ritualId: number, -): Promise<{ - aggregationMismatch: boolean; - initTimestamp: number; - aggregatedTranscriptHash: string; - initiator: string; - dkgSize: number; - id: number; - publicKey: { word1: string; word0: string }; - totalTranscripts: number; - aggregatedTranscript: string; - publicKeyHash: string; - totalAggregations: number; -}> => { - const ritual = await fakeDkgTDecFlowE2E(); - const dkgPkBytes = ritual.dkg.publicKey().toBytes(); - return { - id: ritualId, - initiator: ritual.validators[0].address.toString(), - dkgSize: ritual.sharesNum, - initTimestamp: 0, - totalTranscripts: ritual.receivedMessages.length, - totalAggregations: ritual.sharesNum, // Assuming the ritual is finished - aggregatedTranscriptHash: keccak256(ritual.serverAggregate.toBytes()), - aggregationMismatch: false, // Assuming the ritual is correct - aggregatedTranscript: toHexString(ritual.serverAggregate.toBytes()), - publicKey: { - word0: toHexString(dkgPkBytes.slice(0, 32)), - word1: toHexString(dkgPkBytes.slice(32, 48)), - }, - publicKeyHash: keccak256(ritual.dkg.publicKey().toBytes()), - }; -}; - -export const mockDkgParticipants = ( - ritualId: number, -): { - participants: DkgParticipant[]; - participantSecrets: Record; -} => { - const ritual = fakeDkgTDecFlowE2E(ritualId); - const label = toBytes(`${ritualId}`); - - const participantSecrets: Record = - Object.fromEntries( - ritual.validators.map(({ address }) => { - const participantSecret = SessionSecretFactory.random().makeKey(label); - return [address.toString(), participantSecret]; - }), - ); - - const participants: DkgParticipant[] = zip( - Object.entries(participantSecrets), - ritual.transcripts, - ).map(([[address, secret], transcript]) => { - return { - provider: address, - aggregated: true, // Assuming all validators already contributed to the aggregate - transcript, - decryptionRequestStaticKey: secret.publicKey(), - } as DkgParticipant; - }); - return { participantSecrets, participants }; -}; - export const mockGetParticipants = ( participants: DkgParticipant[], ): SpyInstance => { @@ -527,36 +336,6 @@ export const mockCbdDecrypt = ( }); }; -export const mockRitualId = 0; - -export const fakeDkgRitual = (ritual: { - dkg: Dkg; - sharesNum: number; - threshold: number; -}) => { - return new DkgRitual( - mockRitualId, - ritual.dkg.publicKey(), - ritual.sharesNum, - ritual.threshold, - DkgRitualState.FINALIZED, - ); -}; - -export const mockGetRitual = (dkgRitual: DkgRitual): SpyInstance => { - return vi.spyOn(DkgClient, 'getRitual').mockImplementation(() => { - return Promise.resolve(dkgRitual); - }); -}; - -export const mockGetFinalizedRitualSpy = ( - dkgRitual: DkgRitual, -): SpyInstance => { - return vi.spyOn(DkgClient, 'getFinalizedRitual').mockImplementation(() => { - return Promise.resolve(dkgRitual); - }); -}; - export const mockGetRitualIdFromPublicKey = (ritualId: number): SpyInstance => { return vi .spyOn(DkgCoordinatorAgent, 'getRitualIdFromPublicKey') diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3e967d04a..b2225788b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,9 +98,9 @@ importers: examples/nextjs: dependencies: - '@nucypher/shared': + '@nucypher/pre': specifier: workspace:* - version: link:../../packages/shared + version: link:../../packages/pre '@types/node': specifier: 20.6.3 version: 20.6.3 @@ -134,18 +134,18 @@ importers: examples/nodejs: dependencies: - '@nucypher/shared': + '@nucypher/pre': specifier: workspace:* - version: link:../../packages/shared + version: link:../../packages/pre ethers: specifier: ^5.7.2 version: 5.7.2 examples/react: dependencies: - '@nucypher/shared': + '@nucypher/pre': specifier: workspace:* - version: link:../../packages/shared + version: link:../../packages/pre ethers: specifier: ^5.7.2 version: 5.7.2 @@ -171,9 +171,9 @@ importers: examples/webpack-5: dependencies: - '@nucypher/shared': + '@nucypher/pre': specifier: workspace:* - version: link:../../packages/shared + version: link:../../packages/pre ethers: specifier: ^5.7.2 version: 5.7.2