diff --git a/.gitignore b/.gitignore index d25821bc7..7ebab6644 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,4 @@ examples/*/pnpm-lock.yaml pnpm-debug.log docs-json ./docs -.env .next diff --git a/packages/pre/src/characters/enrico.ts b/packages/pre/src/characters/enrico.ts index a617ffc97..a25e2d5e6 100644 --- a/packages/pre/src/characters/enrico.ts +++ b/packages/pre/src/characters/enrico.ts @@ -1,39 +1,26 @@ import { MessageKit, PublicKey, SecretKey } from '@nucypher/nucypher-core'; -import { ConditionExpression, toBytes } from '@nucypher/shared'; +import { 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, - ) { + constructor(encryptingKey: PublicKey, verifyingKey?: SecretKey) { 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; - } - + public encryptMessage(plaintext: Uint8Array | string): MessageKit { return new MessageKit( this.encryptingKey, plaintext instanceof Uint8Array ? plaintext : toBytes(plaintext), - withConditions ? withConditions.toWASMConditions() : null, + null, ); } } diff --git a/packages/pre/src/characters/index.ts b/packages/pre/src/characters/index.ts index ab6a5bad7..add1486ba 100644 --- a/packages/pre/src/characters/index.ts +++ b/packages/pre/src/characters/index.ts @@ -1,4 +1,3 @@ export * from './alice'; export * from './bob'; export * from './enrico'; -export * from './pre-recipient'; diff --git a/packages/pre/src/characters/pre-recipient.ts b/packages/pre/src/characters/pre-recipient.ts deleted file mode 100644 index ab46b9443..000000000 --- a/packages/pre/src/characters/pre-recipient.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { - Conditions, - EncryptedTreasureMap, - MessageKit, - PublicKey, - SecretKey, - Signer, -} from '@nucypher/nucypher-core'; -import { - base64ToU8Receiver, - ConditionContext, - ConditionExpression, - PorterClient, - 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; - encryptedTreasureMapBytes: Uint8Array; - publisherVerifyingKeyBytes: Uint8Array; - bobSecretKeyBytes: Uint8Array; -}; - -export class PreDecrypter { - // private readonly verifyingKey: Keyring; - - constructor( - private readonly porter: PorterClient, - private readonly keyring: Keyring, - private readonly policyEncryptingKey: PublicKey, - private readonly publisherVerifyingKey: PublicKey, - private readonly encryptedTreasureMap: EncryptedTreasureMap, - ) {} - - public static create( - porterUri: string, - secretKey: SecretKey, - policyEncryptingKey: PublicKey, - publisherVerifyingKey: PublicKey, - encryptedTreasureMap: EncryptedTreasureMap, - ): PreDecrypter { - return new PreDecrypter( - new PorterClient(porterUri), - new Keyring(secretKey), - policyEncryptingKey, - publisherVerifyingKey, - encryptedTreasureMap, - ); - } - - public get decryptingKey(): PublicKey { - return this.keyring.publicKey; - } - - public get signer(): Signer { - return this.keyring.signer; - } - - public decrypt(messageKit: MessageKit | PolicyMessageKit): Uint8Array { - return this.keyring.decrypt(messageKit); - } - - public async retrieveAndDecrypt( - provider: ethers.providers.Provider, - signer: ethers.Signer, - messageKits: readonly MessageKit[], - ): Promise { - const policyMessageKits = await this.retrieve( - provider, - signer, - messageKits, - ); - - policyMessageKits.forEach((mk: PolicyMessageKit) => { - if (!mk.isDecryptableByReceiver()) { - const errorMsg = `Not enough cFrags retrieved to open capsule ${mk.capsule}.`; - if (Object.values(mk.errors).length > 0) { - const ursulasWithErrors = Object.entries(mk.errors).map( - ([address, error]) => `${address} - ${error}`, - ); - throw Error( - `${errorMsg} Some Ursulas have failed with errors:\n${ursulasWithErrors.join( - '\n', - )}`, - ); - } else { - throw Error(errorMsg); - } - } - }); - - return policyMessageKits.map((mk) => this.keyring.decrypt(mk)); - } - - public async retrieve( - provider: ethers.providers.Provider, - signer: ethers.Signer, - messageKits: readonly MessageKit[], - ): Promise { - const treasureMap = this.encryptedTreasureMap.decrypt( - this.keyring.secretKey, - this.publisherVerifyingKey, - ); - - // concat into single array of conditions - const conditions = messageKits - .map((mk) => mk.conditions) - .filter((condition): condition is Conditions => !!condition) - .map((condition) => ConditionExpression.fromJSON(condition.toString())) - .reduce((acc: ConditionExpression[], val) => acc.concat(val), []) - .map((condExpr: ConditionExpression) => condExpr.condition); - - const conditionContext = new ConditionContext( - provider, - conditions, - {}, - signer, - ); - - const policyMessageKits = messageKits.map((mk) => - PolicyMessageKit.fromMessageKit( - mk, - this.policyEncryptingKey, - treasureMap.threshold, - ), - ); - - const retrievalKits = policyMessageKits.map((pk) => pk.asRetrievalKit()); - const conditionContextJSON = conditionContext - ? await conditionContext.toJson() - : undefined; - const retrieveCFragsResponses = await this.porter.retrieveCFrags( - treasureMap, - retrievalKits, - this.publisherVerifyingKey, - this.decryptingKey, - this.keyring.publicKey, - conditionContextJSON, - ); - - return zip(policyMessageKits, retrieveCFragsResponses).map((pair) => { - const [messageKit, { cFrags, errors }] = pair; - const vcFrags = Object.keys(cFrags).map((address) => { - const verified = cFrags[address].verify( - messageKit.capsule, - this.publisherVerifyingKey, - this.policyEncryptingKey, - this.decryptingKey, - ); - return [address, verified]; - }); - const retrievalResult = new RetrievalResult( - Object.fromEntries(vcFrags), - errors, - ); - return messageKit.withResult(retrievalResult); - }); - } - - public toObj(): PreDecrypterJSON { - return { - porterUri: this.porter.porterUrl.toString(), - policyEncryptingKeyBytes: this.policyEncryptingKey.toCompressedBytes(), - encryptedTreasureMapBytes: this.encryptedTreasureMap.toBytes(), - publisherVerifyingKeyBytes: - this.publisherVerifyingKey.toCompressedBytes(), - bobSecretKeyBytes: this.keyring.secretKey.toBEBytes(), - }; - } - - public toJSON(): string { - return toJSON(this.toObj()); - } - - public static fromObj({ - porterUri, - policyEncryptingKeyBytes, - encryptedTreasureMapBytes, - publisherVerifyingKeyBytes, - bobSecretKeyBytes, - }: PreDecrypterJSON) { - return new PreDecrypter( - new PorterClient(porterUri), - new Keyring(SecretKey.fromBEBytes(bobSecretKeyBytes)), - PublicKey.fromCompressedBytes(policyEncryptingKeyBytes), - PublicKey.fromCompressedBytes(publisherVerifyingKeyBytes), - EncryptedTreasureMap.fromBytes(encryptedTreasureMapBytes), - ); - } - - public static fromJSON(json: string) { - const config = JSON.parse(json, base64ToU8Receiver); - return PreDecrypter.fromObj(config); - } - - public equals(other: PreDecrypter): boolean { - return [ - this.porter.porterUrl.toString() === other.porter.porterUrl.toString(), - this.policyEncryptingKey.equals(other.policyEncryptingKey), - this.encryptedTreasureMap.equals(other.encryptedTreasureMap), - this.publisherVerifyingKey.equals(other.publisherVerifyingKey), - ].every(Boolean); - } -} diff --git a/packages/shared/src/cohort.ts b/packages/pre/src/cohort.ts similarity index 91% rename from packages/shared/src/cohort.ts rename to packages/pre/src/cohort.ts index 6807841a5..43e820f8b 100644 --- a/packages/shared/src/cohort.ts +++ b/packages/pre/src/cohort.ts @@ -1,6 +1,4 @@ -import { PorterClient } from './porter'; -import { ChecksumAddress } from './types'; -import { objectEquals } from './utils'; +import { ChecksumAddress, objectEquals, PorterClient } from '@nucypher/shared'; export type CohortJSON = { ursulaAddresses: ChecksumAddress[]; diff --git a/packages/pre/src/index.ts b/packages/pre/src/index.ts index ca4deb863..4f676b1ca 100644 --- a/packages/pre/src/index.ts +++ b/packages/pre/src/index.ts @@ -1,7 +1,5 @@ export { - Cohort, PorterClient, - conditions, fromHexString, getPorterUri, toBytes, @@ -20,8 +18,6 @@ export { initialize, } from '@nucypher/nucypher-core'; -export { DeployedPreStrategy, PreStrategy } from './pre-strategy'; - -export { Alice, Bob, PreDecrypter } from './characters'; - +export { Alice, Bob, Enrico } from './characters'; +export { Cohort } from './cohort'; export { EnactedPolicy } from './policy'; diff --git a/packages/pre/src/policy.ts b/packages/pre/src/policy.ts index 2494b9977..9ce220154 100644 --- a/packages/pre/src/policy.ts +++ b/packages/pre/src/policy.ts @@ -124,7 +124,7 @@ export class BlockchainPolicy { public async generatePreEnactedPolicy( ursulas: readonly Ursula[], ): Promise { - if (ursulas.length != this.verifiedKFrags.length) { + if (ursulas.length !== this.verifiedKFrags.length) { throw new Error( `Number of ursulas must match number of verified kFrags: ${this.verifiedKFrags.length}`, ); diff --git a/packages/pre/src/pre-strategy.ts b/packages/pre/src/pre-strategy.ts deleted file mode 100644 index b2e7fec0c..000000000 --- a/packages/pre/src/pre-strategy.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { PublicKey, SecretKey } from '@nucypher/nucypher-core'; -import { - base64ToU8Receiver, - Cohort, - CohortJSON, - ConditionExpression, - toJSON, -} from '@nucypher/shared'; -import { ethers } from 'ethers'; - -import { - Alice, - Bob, - Enrico, - PreDecrypter, - PreDecrypterJSON, -} from './characters'; -import { EnactedPolicy } from './policy'; - -export type PreStrategyJSON = { - cohort: CohortJSON; - aliceSecretKeyBytes: Uint8Array; - bobSecretKeyBytes: Uint8Array; - startDate: Date; - endDate: Date; -}; - -export type DeployedPreStrategyJSON = { - cohortConfig: CohortJSON; - decrypterJSON: PreDecrypterJSON; - policyKeyBytes: Uint8Array; -}; - -export class PreStrategy { - private constructor( - public readonly cohort: Cohort, - private readonly aliceSecretKey: SecretKey, - private readonly bobSecretKey: SecretKey, - private readonly startDate: Date, - private readonly endDate: Date, - ) {} - - public static create( - cohort: Cohort, - aliceSecretKey?: SecretKey, - bobSecretKey?: SecretKey, - startDate?: Date, - endDate?: Date, - ) { - if (!aliceSecretKey) { - aliceSecretKey = SecretKey.random(); - } - if (!bobSecretKey) { - bobSecretKey = SecretKey.random(); - } - if (!startDate) { - startDate = new Date(Date.now()); - } - if (!endDate) { - endDate = new Date(Date.now() + 1000 * 60 * 60 * 24 * 30); - } - return new PreStrategy( - cohort, - aliceSecretKey, - bobSecretKey, - startDate, - endDate, - ); - } - - public async deploy( - provider: ethers.providers.Provider, - signer: ethers.Signer, - label: string, - threshold = Math.floor(this.cohort.numUrsulas / 2) + 1, - shares = this.cohort.numUrsulas, - ): Promise { - 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 alice = Alice.fromSecretKey(this.aliceSecretKey); - const bob = new Bob(this.bobSecretKey); - const policyParams = { - bob, - label, - threshold, - shares, - startDate: this.startDate, - endDate: this.endDate, - }; - const policy = await alice.grant( - provider, - signer, - porterUri, - policyParams, - this.cohort.ursulaAddresses, - ); - return DeployedPreStrategy.create(this.cohort, policy, this.bobSecretKey); - } - - public static fromJSON(json: string) { - const config = JSON.parse(json, base64ToU8Receiver); - config.startDate = new Date(config.startDate); - config.endDate = new Date(config.endDate); - return PreStrategy.fromObj(config); - } - - public toJSON() { - return toJSON(this.toObj()); - } - - public static fromObj({ - cohort, - aliceSecretKeyBytes, - bobSecretKeyBytes, - startDate, - endDate, - }: PreStrategyJSON) { - return new PreStrategy( - Cohort.fromObj(cohort), - SecretKey.fromBEBytes(aliceSecretKeyBytes), - SecretKey.fromBEBytes(bobSecretKeyBytes), - new Date(startDate), - new Date(endDate), - ); - } - - public toObj(): PreStrategyJSON { - return { - cohort: this.cohort.toObj(), - aliceSecretKeyBytes: this.aliceSecretKey.toBEBytes(), - bobSecretKeyBytes: this.bobSecretKey.toBEBytes(), - startDate: this.startDate, - endDate: this.endDate, - }; - } - - public equals(other: PreStrategy) { - return [ - this.cohort.equals(other.cohort), - this.aliceSecretKey.equals(other.aliceSecretKey), - this.bobSecretKey.equals(other.bobSecretKey), - this.startDate.toString() === other.startDate.toString(), - this.endDate.toString() === other.endDate.toString(), - ].every(Boolean); - } -} - -export class DeployedPreStrategy { - private constructor( - public readonly cohort: Cohort, - public readonly decrypter: PreDecrypter, - public readonly policyKey: PublicKey, - ) {} - - public static create( - cohort: Cohort, - policy: EnactedPolicy, - bobSecretKey: SecretKey, - ) { - const decrypter = PreDecrypter.create( - cohort.porterUri, - bobSecretKey, - policy.policyKey, - policy.aliceVerifyingKey, - policy.encryptedTreasureMap, - ); - return new DeployedPreStrategy(cohort, decrypter, policy.policyKey); - } - - public makeEncrypter(conditionExpr: ConditionExpression): Enrico { - return new Enrico(this.policyKey, undefined, conditionExpr); - } - - public static fromJSON(json: string) { - const config = JSON.parse(json, base64ToU8Receiver); - return DeployedPreStrategy.fromObj(config); - } - - public toJSON() { - return toJSON(this.toObj()); - } - - public static fromObj({ - cohortConfig, - decrypterJSON, - policyKeyBytes, - }: DeployedPreStrategyJSON) { - const cohort = Cohort.fromObj(cohortConfig); - const decrypter = PreDecrypter.fromObj(decrypterJSON); - const policyKey = PublicKey.fromCompressedBytes(policyKeyBytes); - return new DeployedPreStrategy(cohort, decrypter, policyKey); - } - - public toObj(): DeployedPreStrategyJSON { - return { - cohortConfig: this.cohort.toObj(), - decrypterJSON: this.decrypter.toObj(), - policyKeyBytes: this.policyKey.toCompressedBytes(), - }; - } - - public equals(other: DeployedPreStrategy) { - return [ - this.cohort.equals(other.cohort), - this.decrypter.equals(other.decrypter), - this.policyKey.equals(other.policyKey), - ].every(Boolean); - } -} diff --git a/packages/pre/test/acceptance/alice-grants.test.ts b/packages/pre/test/acceptance/alice-grants.test.ts index 6c15b7358..69230c2f8 100644 --- a/packages/pre/test/acceptance/alice-grants.test.ts +++ b/packages/pre/test/acceptance/alice-grants.test.ts @@ -18,8 +18,7 @@ import { } from '@nucypher/test-utils'; import { beforeEach, describe, expect, it } from 'vitest'; -import { EnactedPolicy, toBytes } from '../../src'; -import { Enrico } from '../../src/characters'; +import { EnactedPolicy, Enrico, toBytes } from '../../src'; import { fakeAlice, fakeBob, @@ -29,7 +28,7 @@ import { mockMakeTreasureMap, mockPublishToBlockchain, reencryptKFrags, -} from '../test-utils'; +} from '../utils'; describe('story: alice shares message with bob through policy', () => { const message = 'secret-message-from-alice'; @@ -105,7 +104,7 @@ describe('story: alice shares message with bob through policy', () => { it('enrico encrypts the message', () => { const enrico = new Enrico(policyEncryptingKey); - encryptedMessage = enrico.encryptMessagePre(toBytes(message)); + encryptedMessage = enrico.encryptMessage(toBytes(message)); }); it('bob retrieves and decrypts the message', async () => { diff --git a/packages/pre/test/acceptance/delay-enact.test.ts b/packages/pre/test/acceptance/delay-enact.test.ts index cafa6f518..74a47e72b 100644 --- a/packages/pre/test/acceptance/delay-enact.test.ts +++ b/packages/pre/test/acceptance/delay-enact.test.ts @@ -13,7 +13,7 @@ import { mockEncryptTreasureMap, mockGenerateKFrags, mockPublishToBlockchain, -} from '../test-utils'; +} from '../utils'; test('story: alice creates a policy but someone else enacts it', () => { const threshold = 2; diff --git a/packages/shared/test/cohort.test.ts b/packages/pre/test/cohort.test.ts similarity index 84% rename from packages/shared/test/cohort.test.ts rename to packages/pre/test/cohort.test.ts index 8fff3b734..1baf6189c 100644 --- a/packages/shared/test/cohort.test.ts +++ b/packages/pre/test/cohort.test.ts @@ -1,7 +1,10 @@ -import { fakeUrsulas, makeCohort } from '@nucypher/test-utils'; +import { initialize } from '@nucypher/shared'; +import { fakeUrsulas } from '@nucypher/test-utils'; import { beforeAll, describe, expect, it } from 'vitest'; -import { Cohort, initialize } from '../src'; +import { Cohort } from '../src'; + +import { makeCohort } from './utils'; describe('Cohort', () => { beforeAll(async () => { diff --git a/packages/pre/test/docs.test.ts b/packages/pre/test/docs.test.ts deleted file mode 100644 index fd32cddb4..000000000 --- a/packages/pre/test/docs.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { - ConditionExpression, - ContractCondition, - ContractConditionProps, - ERC721Ownership, -} from '@nucypher/shared'; -import { - fakeProvider, - fakeUrsulas, - mockDetectEthereumProvider, - mockGetUrsulas, - mockRetrieveAndDecrypt, -} from '@nucypher/test-utils'; -import { providers } from 'ethers'; -import { beforeAll, describe, expect, it, vi } from 'vitest'; - -import { - Cohort, - getPorterUri, - initialize, - PreStrategy, - SecretKey, - toBytes, -} from '../src'; - -import { - mockEncryptTreasureMap, - mockGenerateKFrags, - mockMakeTreasureMap, - mockPublishToBlockchain, -} from './test-utils'; - -describe('doc tests', async () => { - beforeAll(async () => { - await initialize(); - }); - - it('runs get started example', async () => { - const detectEthereumProvider = mockDetectEthereumProvider(); - const getUrsulasSpy = mockGetUrsulas(); - const generateKFragsSpy = mockGenerateKFrags(); - const publishToBlockchainSpy = mockPublishToBlockchain(); - const makeTreasureMapSpy = mockMakeTreasureMap(); - const encryptTreasureMapSpy = mockEncryptTreasureMap(); - - vi.spyOn(providers, 'Web3Provider').mockImplementation(() => - fakeProvider(SecretKey.random().toBEBytes()), - ); - - // - // Start of the code example - // - - // 2. Build a Cohort - const porterUri = getPorterUri('tapir'); - const numUrsulas = 5; - const newCohort = await Cohort.create(porterUri, numUrsulas); - - // 3. Specify default conditions - const NFTOwnership = new ERC721Ownership({ - contractAddress: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D', - chain: 5, // Tapir network uses Görli testnet - parameters: [5954], - }); - - const conditions = new ConditionExpression( - NFTOwnership, - // Other conditions can be added here - ); - - // 4. Build a Strategy - const newStrategy = PreStrategy.create(newCohort); - - const MMprovider = await detectEthereumProvider(); - const mumbai = providers.getNetwork(80001); - - const provider = new providers.Web3Provider(MMprovider, mumbai); - const signer = provider.getSigner(); - const newDeployed = await newStrategy.deploy(provider, signer, 'test'); - - // 5. Encrypt the plaintext & update conditions - const NFTBalanceConfig: ContractConditionProps = { - conditionType: 'contract', - contractAddress: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D', - standardContractType: 'ERC721', - chain: 5, - method: 'balanceOf', - parameters: [':userAddress'], - returnValueTest: { - comparator: '>=', - value: 3, - }, - }; - const NFTBalance = new ContractCondition(NFTBalanceConfig); - const newConditions = new ConditionExpression(NFTBalance); - const plaintext = 'this is a secret'; - const encrypter = newDeployed.makeEncrypter(newConditions); - const encryptedMessageKit = encrypter.encryptMessagePre(plaintext); - - // Mocking - Not a part of any code example - const retrieveCFragsSpy = mockRetrieveAndDecrypt( - makeTreasureMapSpy, - encryptedMessageKit, - ); - - // 6. Request decryption rights - const decryptedMessage = await newDeployed.decrypter.retrieveAndDecrypt( - provider, - signer, - [encryptedMessageKit], - ); - - // - // End of the code example - // - - const expectedAddresses = fakeUrsulas().map((u) => u.checksumAddress); - const condObj = conditions.condition.toObj(); - expect(newCohort.ursulaAddresses).toEqual(expectedAddresses); - expect(condObj.parameters).toEqual([5954]); - expect(condObj.chain).toEqual(NFTBalanceConfig.chain); - expect(condObj.contractAddress).toEqual(NFTBalanceConfig.contractAddress); - expect(publishToBlockchainSpy).toHaveBeenCalled(); - expect(getUrsulasSpy).toHaveBeenCalledTimes(2); - expect(generateKFragsSpy).toHaveBeenCalled(); - expect(encryptTreasureMapSpy).toHaveBeenCalled(); - expect(makeTreasureMapSpy).toHaveBeenCalled(); - expect(retrieveCFragsSpy).toHaveBeenCalled(); - expect(decryptedMessage[0]).toEqual(toBytes(plaintext)); - }); -}); diff --git a/packages/pre/test/enrico.test.ts b/packages/pre/test/enrico.test.ts index 41fa7c5a6..b911ef544 100644 --- a/packages/pre/test/enrico.test.ts +++ b/packages/pre/test/enrico.test.ts @@ -1,22 +1,12 @@ // Disabling because we want to access Alice.keyring which is a private property /* eslint-disable @typescript-eslint/no-explicit-any */ -import { - bytesEqual, - fakeAlice, - fakeBob, - fromBytes, - reencryptKFrags, -} from '@nucypher/test-utils'; +import { bytesEqual, fromBytes } from '@nucypher/test-utils'; import { expect, test } from 'vitest'; -import { - ConditionExpression, - Enrico, - ERC721Ownership, - PolicyMessageKit, - RetrievalResult, - toBytes, -} from '../src'; +import { Enrico, toBytes } from '../src'; +import { PolicyMessageKit, RetrievalResult } from '../src/kits'; + +import { fakeAlice, fakeBob, reencryptKFrags } from './utils'; test('enrico', () => { test('alice decrypts message encrypted by enrico', async () => { @@ -26,7 +16,7 @@ test('enrico', () => { const policyKey = alice.getPolicyEncryptingKeyFromLabel(label); const enrico = new Enrico(policyKey); - const encrypted = enrico.encryptMessagePre(toBytes(message)); + const encrypted = enrico.encryptMessage(toBytes(message)); const aliceKeyring = (alice as any).keyring; const aliceSk = await aliceKeyring.getSecretKeyFromLabel(label); @@ -44,7 +34,7 @@ test('enrico', () => { const plaintext = 'Plaintext message'; const plaintextBytes = toBytes(plaintext); - const encrypted = enrico.encryptMessagePre(plaintextBytes); + const encrypted = enrico.encryptMessage(plaintextBytes); // Alice can decrypt capsule she created const aliceSk = await (alice as any).keyring.getSecretKeyFromLabel(label); @@ -93,62 +83,4 @@ test('enrico', () => { const decrypted = bob.decrypt(pk); expect(bytesEqual(decrypted, plaintextBytes)).toBeTruthy(); }); - - test('enrico generates a message kit with conditions', async () => { - const label = 'fake-label'; - const message = 'fake-message'; - const alice = fakeAlice(); - - const policyKey = alice.getPolicyEncryptingKeyFromLabel(label); - - const ownsBufficornNFT = new ERC721Ownership({ - contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', - parameters: [3591], - chain: 5, - }); - - const conditions = new ConditionExpression(ownsBufficornNFT); - - const enrico = new Enrico(policyKey, undefined, conditions); - const encrypted = enrico.encryptMessagePre(toBytes(message)); - - const aliceKeyring = (alice as any).keyring; - const aliceSk = await aliceKeyring.getSecretKeyFromLabel(label); - const alicePlaintext = encrypted.decrypt(aliceSk); - expect(alicePlaintext).toEqual(alicePlaintext); - }); - - test('can overwrite conditions at encryption time', async () => { - const label = 'fake-label'; - const message = 'fake-message'; - const alice = fakeAlice(); - - const policyKey = alice.getPolicyEncryptingKeyFromLabel(label); - - const ownsBufficornNFT = new ERC721Ownership({ - contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', - chain: 5, - parameters: [3591], - }); - - const ownsNonsenseNFT = new ERC721Ownership({ - contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', - chain: 5, - parameters: [6969], - }); - - const conditions = new ConditionExpression(ownsBufficornNFT); - const updatedConditions = new ConditionExpression(ownsNonsenseNFT); - - const enrico = new Enrico(policyKey, undefined, conditions); - const encrypted = enrico.encryptMessagePre( - toBytes(message), - updatedConditions, - ); - - const aliceKeyring = (alice as any).keyring; - const aliceSk = await aliceKeyring.getSecretKeyFromLabel(label); - const alicePlaintext = encrypted.decrypt(aliceSk); - expect(alicePlaintext).toEqual(alicePlaintext); - }); }); diff --git a/packages/pre/test/message-kit.test.ts b/packages/pre/test/message-kit.test.ts index 7b6bea004..4ca01d0a7 100644 --- a/packages/pre/test/message-kit.test.ts +++ b/packages/pre/test/message-kit.test.ts @@ -1,8 +1,9 @@ -import { fakeBob } from '@nucypher/test-utils'; import { expect, test } from 'vitest'; import { MessageKit, toBytes } from '../src'; +import { fakeBob } from './utils'; + test('message kit', () => { test('bob decrypts', () => { const bob = fakeBob(); diff --git a/packages/pre/test/pre-strategy.test.ts b/packages/pre/test/pre-strategy.test.ts deleted file mode 100644 index f5f1940f9..000000000 --- a/packages/pre/test/pre-strategy.test.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { SecretKey, VerifiedKeyFrag } from '@nucypher/nucypher-core'; -import { Ursula } from '@nucypher/shared'; -import { - aliceSecretKeyBytes, - bobSecretKeyBytes, - fakeProvider, - fakeSigner, - fakeUrsulas, - makeCohort, - mockGetUrsulas, - mockRetrieveCFragsRequest, -} from '@nucypher/test-utils'; -import { afterEach, expect, test, vi } from 'vitest'; - -import { - conditions, - DeployedPreStrategy, - PreDecrypter, - PreStrategy, - toBytes, -} from '../src'; - -import { - mockEncryptTreasureMap, - mockGenerateKFrags, - mockMakeTreasureMap, - mockPublishToBlockchain, -} from './test-utils'; - -// Shared test variables -const ownsNFT = new conditions.ERC721Ownership({ - contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', - parameters: [3591], - chain: 5, -}); -const conditionExpr = new conditions.ConditionExpression(ownsNFT); - -const makePreStrategy = async () => { - const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); - const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); - const cohort = await makeCohort(fakeUrsulas()); - const strategy = PreStrategy.create(cohort, aliceSecretKey, bobSecretKey); - expect(strategy.cohort).toEqual(cohort); - return strategy; -}; - -const makeDeployedPreStrategy = async () => { - const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); - const aliceSigner = fakeSigner(aliceSecretKey.toBEBytes()); - const aliceProvider = fakeProvider(aliceSecretKey.toBEBytes()); - - const strategy = await makePreStrategy(); - const generateKFragsSpy = mockGenerateKFrags(); - const publishToBlockchainSpy = mockPublishToBlockchain(); - const makeTreasureMapSpy = mockMakeTreasureMap(); - const encryptTreasureMapSpy = mockEncryptTreasureMap(); - - const deployedStrategy = await strategy.deploy( - aliceProvider, - aliceSigner, - 'test', - ); - - expect(generateKFragsSpy).toHaveBeenCalled(); - expect(publishToBlockchainSpy).toHaveBeenCalled(); - expect(makeTreasureMapSpy).toHaveBeenCalled(); - expect(encryptTreasureMapSpy).toHaveBeenCalled(); - - expect(deployedStrategy.cohort).toEqual(strategy.cohort); - - const ursulaAddresses = ( - makeTreasureMapSpy.mock.calls[0][0] as readonly Ursula[] - ).map((u) => u.checksumAddress); - const verifiedKFrags = makeTreasureMapSpy.mock - .calls[0][1] as readonly VerifiedKeyFrag[]; - - return { deployedStrategy, ursulaAddresses, verifiedKFrags }; -}; - -test('pre strategy', () => { - test('PreStrategy', () => { - afterEach(() => { - vi.restoreAllMocks(); - }); - - test('creates a strategy', async () => { - await makePreStrategy(); - }); - - test('deploys a strategy', async () => { - await makeDeployedPreStrategy(); - }); - - test('serialization', () => { - test('serializes to plain object', async () => { - const strategy = await makePreStrategy(); - const asObject = strategy.toObj(); - const fromObject = PreStrategy.fromObj(asObject); - expect(fromObject.equals(strategy)).toBeTruthy(); - }); - - test('serializes to JSON', async () => { - const strategy = await makePreStrategy(); - const asJson = strategy.toJSON(); - const fromJSON = PreStrategy.fromJSON(asJson); - expect(fromJSON.equals(strategy)).toBeTruthy(); - }); - }); - }); - - test('PreDeployedStrategy', () => { - afterEach(() => { - vi.restoreAllMocks(); - }); - - test('encrypts and decrypts', async () => { - const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); - const bobSigner = fakeSigner(bobSecretKey.toBEBytes()); - const bobProvider = fakeProvider(bobSecretKey.toBEBytes()); - const { deployedStrategy, ursulaAddresses, verifiedKFrags } = - await makeDeployedPreStrategy(); - - const plaintext = 'this is a secret'; - const encryptedMessageKit = deployedStrategy - .makeEncrypter(conditionExpr) - .encryptMessagePre(plaintext); - - // Setup mocks for `retrieveAndDecrypt` - const getUrsulasSpy = mockGetUrsulas(); - const retrieveCFragsSpy = mockRetrieveCFragsRequest( - ursulaAddresses, - verifiedKFrags, - encryptedMessageKit.capsule, - ); - - const decryptedMessage = - await deployedStrategy.decrypter.retrieveAndDecrypt( - bobProvider, - bobSigner, - [encryptedMessageKit], - ); - expect(getUrsulasSpy).toHaveBeenCalled(); - expect(retrieveCFragsSpy).toHaveBeenCalled(); - expect(decryptedMessage[0]).toEqual(toBytes(plaintext)); - }); - - test('serialization', () => { - test('serializes to a plain object', async () => { - const { deployedStrategy } = await makeDeployedPreStrategy(); - const asObj = deployedStrategy.toObj(); - const fromJson = DeployedPreStrategy.fromObj(asObj); - expect(fromJson.equals(deployedStrategy)).toBeTruthy(); - }); - - test('serializes to a JSON', async () => { - const { deployedStrategy } = await makeDeployedPreStrategy(); - const asJson = deployedStrategy.toJSON(); - const fromJson = DeployedPreStrategy.fromJSON(asJson); - expect(fromJson.equals(deployedStrategy)).toBeTruthy(); - }); - }); - }); - - test('PreDecrypter', () => { - test('serializes to a plain object', async () => { - const { deployedStrategy } = await makeDeployedPreStrategy(); - const asObj = deployedStrategy.decrypter.toObj(); - const fromJson = PreDecrypter.fromObj(asObj); - expect(fromJson.equals(deployedStrategy.decrypter)).toBeTruthy(); - }); - - test('serializes to JSON', async () => { - const { deployedStrategy } = await makeDeployedPreStrategy(); - const asJson = deployedStrategy.decrypter.toJSON(); - const fromJson = PreDecrypter.fromJSON(asJson); - expect(fromJson.equals(deployedStrategy.decrypter)).toBeTruthy(); - }); - }); -}); diff --git a/packages/pre/test/pre.test.ts b/packages/pre/test/pre.test.ts index 79923486a..35b1b4f41 100644 --- a/packages/pre/test/pre.test.ts +++ b/packages/pre/test/pre.test.ts @@ -1,18 +1,12 @@ import { CapsuleFrag, reencrypt } from '@nucypher/nucypher-core'; -import { - CompoundCondition, - ConditionExpression, - ERC721Ownership, - zip, -} from '@nucypher/shared'; +import { zip } from '@nucypher/shared'; import { fakeUrsulas } from '@nucypher/test-utils'; import { beforeAll, expect, test } from 'vitest'; -import { Alice, Bob, MessageKit, toBytes } from '../src'; -import { Enrico } from '../src/characters'; +import { Alice, Bob, Enrico, MessageKit, toBytes } from '../src'; import { PolicyMessageKit, RetrievalResult } from '../src/kits'; -import { fakeAlice, fakeBob, reencryptKFrags } from './test-utils'; +import { fakeAlice, fakeBob, reencryptKFrags } from './utils'; test('proxy reencryption', () => { let alice: Alice; @@ -61,7 +55,7 @@ test('proxy reencryption', () => { const policyEncryptingKey = alice.getPolicyEncryptingKeyFromLabel(label); const enrico = new Enrico(policyEncryptingKey); - const encryptedMessage = enrico.encryptMessagePre(plaintext); + const encryptedMessage = enrico.encryptMessage(plaintext); const ursulaAddresses = fakeUrsulas().map( (ursula) => ursula.checksumAddress, @@ -93,25 +87,8 @@ test('proxy reencryption', () => { 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 enrico = new Enrico(policyEncryptingKey); + const encryptedMessage = enrico.encryptMessage(plaintext); const ursulaAddresses = fakeUrsulas().map( (ursula) => ursula.checksumAddress, diff --git a/packages/pre/test/test-utils.ts b/packages/pre/test/utils.ts similarity index 80% rename from packages/pre/test/test-utils.ts rename to packages/pre/test/utils.ts index ca61faa94..b9090456d 100644 --- a/packages/pre/test/test-utils.ts +++ b/packages/pre/test/utils.ts @@ -12,9 +12,11 @@ import { VerifiedCapsuleFrag, VerifiedKeyFrag, } from '@nucypher/nucypher-core'; -import { SpyInstance, vi } from 'vitest'; +import { Ursula } from '@nucypher/shared'; +import { fakeUrsulas, mockGetUrsulas } from '@nucypher/test-utils'; +import { expect, SpyInstance, vi } from 'vitest'; -import { Alice, Bob, toBytes } from '../src'; +import { Alice, Bob, Cohort, toBytes } from '../src'; import { RemoteBob } from '../src/characters'; import { BlockchainPolicy, PreEnactedPolicy } from '../src/policy'; @@ -79,3 +81,12 @@ export const reencryptKFrags = ( export const mockMakeTreasureMap = (): SpyInstance => { return vi.spyOn(BlockchainPolicy.prototype as any, 'makeTreasureMap'); }; + +export const makeCohort = async (ursulas: Ursula[] = fakeUrsulas()) => { + const getUrsulasSpy = mockGetUrsulas(ursulas); + const porterUri = 'https://_this.should.crash'; + const numUrsulas = ursulas.length; + const cohort = await Cohort.create(porterUri, numUrsulas); + expect(getUrsulasSpy).toHaveBeenCalled(); + return cohort; +}; diff --git a/packages/shared/package.json b/packages/shared/package.json index 8c2bc58d3..bc61c6cac 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -33,35 +33,28 @@ "clean": "rm -rf dist", "exports:lint": "ts-unused-exports tsconfig.json --ignoreFiles src/index.ts", "postinstall": "pnpm typechain", - "lint": "eslint --ext .ts src test", + "lint": "eslint --ext .ts src", "lint:fix": "pnpm lint --fix", "package-check": "package-check", - "test": "vitest run", "typechain": "typechain --out-dir=./src/contracts/ethers-typechain --target=ethers-v5 \"abis/**/*.json\"", "typedoc": "typedoc" }, "dependencies": { "@ethersproject/abi": "^5.7.0", - "@ethersproject/abstract-signer": "^5.7.0", "@ethersproject/providers": "^5.7.2", "@nucypher/nucypher-core": "0.13.0-alpha.1", "axios": "^1.5.0", "deep-equal": "^2.2.1", "ethers": "^5.7.2", - "qs": "^6.10.1", - "semver": "^7.5.2", - "zod": "^3.22.1" + "qs": "^6.10.1" }, "devDependencies": { - "@nucypher/test-utils": "workspace:*", "@typechain/ethers-v5": "^11.1.1", "@types/deep-equal": "^1.0.1", "@types/qs": "^6.9.7", - "@types/semver": "^7.5.0", "cz-conventional-changelog": "^3.0.1", "standard-version": "^9.0.0", - "typechain": "^8.3.1", - "vitest": "^0.34.4" + "typechain": "^8.3.1" }, "engines": { "node": ">=18", diff --git a/packages/shared/src/contracts/agents/subscription-manager.ts b/packages/shared/src/contracts/agents/subscription-manager.ts index f249d56e8..88113f355 100644 --- a/packages/shared/src/contracts/agents/subscription-manager.ts +++ b/packages/shared/src/contracts/agents/subscription-manager.ts @@ -23,11 +23,11 @@ export class PreSubscriptionManagerAgent { endTimestamp: number, ownerAddress: ChecksumAddress, ): Promise { - const SubscriptionManager = await this.connectReadWrite(provider, signer); + const subscriptionManager = await this.connectReadWrite(provider, signer); const overrides = { value: valueInWei.toString(), }; - const estimatedGas = await SubscriptionManager.estimateGas.createPolicy( + const estimatedGas = await subscriptionManager.estimateGas.createPolicy( ethersUtils.hexlify(policyId), ownerAddress, size, @@ -35,7 +35,7 @@ export class PreSubscriptionManagerAgent { endTimestamp, overrides, ); - const tx = await SubscriptionManager.createPolicy( + const tx = await subscriptionManager.createPolicy( ethersUtils.hexlify(policyId), ownerAddress, size, @@ -53,8 +53,8 @@ export class PreSubscriptionManagerAgent { startTimestamp: number, endTimestamp: number, ): Promise { - const SubscriptionManager = await this.connectReadOnly(provider); - return await SubscriptionManager.getPolicyCost( + const subscriptionManager = await this.connectReadOnly(provider); + return await subscriptionManager.getPolicyCost( size, startTimestamp, endTimestamp, diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 1e1650de2..800d2ac5e 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1,15 +1,9 @@ -export * from './cohort'; -export * from './conditions'; export * from './contracts'; export * from './porter'; export * from './types'; export * from './utils'; export * from './web3'; -import * as conditions from './conditions'; - -export { conditions }; - // Re-exports export { Ciphertext, diff --git a/packages/shared/src/web3.ts b/packages/shared/src/web3.ts index a55508f8f..c4bdcee12 100644 --- a/packages/shared/src/web3.ts +++ b/packages/shared/src/web3.ts @@ -8,35 +8,9 @@ export enum ChainId { } export const toCanonicalAddress = (address: string): Uint8Array => { - const ETH_ADDRESS_STRING_PREFIX = '0x'; - const nonPrefixed = address.startsWith(ETH_ADDRESS_STRING_PREFIX) - ? address.substring(ETH_ADDRESS_STRING_PREFIX.length) + const ethAddressStringPrefix = '0x'; + const nonPrefixed = address.startsWith(ethAddressStringPrefix) + ? address.substring(ethAddressStringPrefix.length) : address; return fromHexString(nonPrefixed); }; - -export interface Eip712TypedData { - types: { - Wallet: { name: string; type: string }[]; - }; - domain: { - salt: string; - chainId: number; - name: string; - version: string; - }; - message: { - blockHash: string; - address: string; - blockNumber: number; - signatureText: string; - }; -} - -export interface FormattedTypedData extends Eip712TypedData { - primaryType: 'Wallet'; - types: { - EIP712Domain: { name: string; type: string }[]; - Wallet: { name: string; type: string }[]; - }; -} diff --git a/packages/taco/package.json b/packages/taco/package.json index ed3e82232..c5ee587ae 100644 --- a/packages/taco/package.json +++ b/packages/taco/package.json @@ -38,11 +38,15 @@ "typedoc": "typedoc" }, "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", "@nucypher/nucypher-core": "0.13.0-alpha.1", - "@nucypher/shared": "workspace:*" + "@nucypher/shared": "workspace:*", + "semver": "^7.5.2", + "zod": "^3.22.1" }, "devDependencies": { - "@nucypher/test-utils": "workspace:*" + "@nucypher/test-utils": "workspace:*", + "@types/semver": "^7.5.0" }, "peerDependencies": { "ethers": "^5.7.2" diff --git a/packages/shared/src/conditions/base/contract.ts b/packages/taco/src/conditions/base/contract.ts similarity index 100% rename from packages/shared/src/conditions/base/contract.ts rename to packages/taco/src/conditions/base/contract.ts diff --git a/packages/shared/src/conditions/base/index.ts b/packages/taco/src/conditions/base/index.ts similarity index 100% rename from packages/shared/src/conditions/base/index.ts rename to packages/taco/src/conditions/base/index.ts diff --git a/packages/shared/src/conditions/base/rpc.ts b/packages/taco/src/conditions/base/rpc.ts similarity index 100% rename from packages/shared/src/conditions/base/rpc.ts rename to packages/taco/src/conditions/base/rpc.ts diff --git a/packages/shared/src/conditions/base/shared.ts b/packages/taco/src/conditions/base/shared.ts similarity index 100% rename from packages/shared/src/conditions/base/shared.ts rename to packages/taco/src/conditions/base/shared.ts diff --git a/packages/shared/src/conditions/base/time.ts b/packages/taco/src/conditions/base/time.ts similarity index 100% rename from packages/shared/src/conditions/base/time.ts rename to packages/taco/src/conditions/base/time.ts diff --git a/packages/shared/src/conditions/compound-condition.ts b/packages/taco/src/conditions/compound-condition.ts similarity index 100% rename from packages/shared/src/conditions/compound-condition.ts rename to packages/taco/src/conditions/compound-condition.ts diff --git a/packages/shared/src/conditions/condition-expr.ts b/packages/taco/src/conditions/condition-expr.ts similarity index 89% rename from packages/shared/src/conditions/condition-expr.ts rename to packages/taco/src/conditions/condition-expr.ts index efd6def77..f343d6799 100644 --- a/packages/shared/src/conditions/condition-expr.ts +++ b/packages/taco/src/conditions/condition-expr.ts @@ -1,9 +1,8 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; +import { toJSON } from '@nucypher/shared'; import { ethers } from 'ethers'; import { SemVer } from 'semver'; -import { toJSON } from '../utils'; - import { Condition } from './condition'; import { ConditionContext, CustomContextParam } from './context'; @@ -13,11 +12,11 @@ export type ConditionExpressionJSON = { }; export class ConditionExpression { - public static VERSION = '1.0.0'; + public static version = '1.0.0'; constructor( public readonly condition: Condition, - public readonly version: string = ConditionExpression.VERSION, + public readonly version: string = ConditionExpression.version, ) {} public toObj(): ConditionExpressionJSON { @@ -30,10 +29,10 @@ export class ConditionExpression { public static fromObj(obj: ConditionExpressionJSON): ConditionExpression { const receivedVersion = new SemVer(obj.version); - const currentVersion = new SemVer(ConditionExpression.VERSION); + const currentVersion = new SemVer(ConditionExpression.version); if (receivedVersion.major > currentVersion.major) { throw new Error( - `Version provided, ${obj.version}, is incompatible with current version, ${ConditionExpression.VERSION}`, + `Version provided, ${obj.version}, is incompatible with current version, ${ConditionExpression.version}`, ); } diff --git a/packages/shared/src/conditions/condition.ts b/packages/taco/src/conditions/condition.ts similarity index 97% rename from packages/shared/src/conditions/condition.ts rename to packages/taco/src/conditions/condition.ts index e5c5cc865..4d79e42c6 100644 --- a/packages/shared/src/conditions/condition.ts +++ b/packages/taco/src/conditions/condition.ts @@ -1,7 +1,6 @@ +import { objectEquals } from '@nucypher/shared'; import { z } from 'zod'; -import { objectEquals } from '../utils'; - import { CompoundCondition, ContractCondition, diff --git a/packages/shared/src/conditions/const.ts b/packages/taco/src/conditions/const.ts similarity index 84% rename from packages/shared/src/conditions/const.ts rename to packages/taco/src/conditions/const.ts index 9854c6a13..a7d8dc12c 100644 --- a/packages/shared/src/conditions/const.ts +++ b/packages/taco/src/conditions/const.ts @@ -1,4 +1,4 @@ -import { ChainId } from '../web3'; +import { ChainId } from '@nucypher/shared'; export const USER_ADDRESS_PARAM = ':userAddress'; diff --git a/packages/shared/src/conditions/context/context.ts b/packages/taco/src/conditions/context/context.ts similarity index 92% rename from packages/shared/src/conditions/context/context.ts rename to packages/taco/src/conditions/context/context.ts index a32bd0428..158ccf076 100644 --- a/packages/shared/src/conditions/context/context.ts +++ b/packages/taco/src/conditions/context/context.ts @@ -1,7 +1,7 @@ import { Context, Conditions as WASMConditions } from '@nucypher/nucypher-core'; +import { fromJSON, toJSON } from '@nucypher/shared'; import { ethers } from 'ethers'; -import { fromJSON, toJSON } from '../../utils'; import { Condition } from '../condition'; import { ConditionExpression } from '../condition-expr'; import { USER_ADDRESS_PARAM } from '../const'; @@ -59,32 +59,24 @@ export class ConditionContext { } public toObj = async (): Promise> => { - // First, we want to find all the parameters we need to add - const requestedParameters = new Set(); + const requestedParameters = this.findRequestedParameters(); + const parameters = await this.fillContextParameters(requestedParameters); - // Search conditions for parameters - const conditions = this.conditions.map((cnd) => cnd.toObj()); - const conditionsToCheck = fromJSON( - new WASMConditions(toJSON(conditions)).toString(), + // Ok, so at this point we should have all the parameters we need + // If we don't, we have a problem and we should throw + const missingParameters = Array.from(requestedParameters).filter( + (key) => !parameters[key], ); - for (const cond of conditionsToCheck) { - // Check return value test - const rvt = cond.returnValueTest.value; - if (typeof rvt === 'string' && rvt.startsWith(CONTEXT_PARAM_PREFIX)) { - requestedParameters.add(rvt); - } - - // Check condition parameters - for (const param of cond.parameters ?? []) { - if ( - typeof param === 'string' && - param.startsWith(CONTEXT_PARAM_PREFIX) - ) { - requestedParameters.add(param); - } - } + if (missingParameters.length > 0) { + throw new Error( + `Missing custom context parameter(s): ${missingParameters.join(', ')}`, + ); } + return parameters; + }; + + private async fillContextParameters(requestedParameters: Set) { // Now, we can safely add all the parameters const parameters: Record = {}; @@ -105,20 +97,37 @@ export class ConditionContext { for (const key in this.customParameters) { parameters[key] = this.customParameters[key]; } + return parameters; + } - // Ok, so at this point we should have all the parameters we need - // If we don't, we have a problem and we should throw - const missingParameters = Array.from(requestedParameters).filter( - (key) => !parameters[key], + private findRequestedParameters() { + // First, we want to find all the parameters we need to add + const requestedParameters = new Set(); + + // Search conditions for parameters + const conditions = this.conditions.map((cnd) => cnd.toObj()); + const conditionsToCheck = fromJSON( + new WASMConditions(toJSON(conditions)).toString(), ); - if (missingParameters.length > 0) { - throw new Error( - `Missing custom context parameter(s): ${missingParameters.join(', ')}`, - ); - } + for (const cond of conditionsToCheck) { + // Check return value test + const rvt = cond.returnValueTest.value; + if (typeof rvt === 'string' && rvt.startsWith(CONTEXT_PARAM_PREFIX)) { + requestedParameters.add(rvt); + } - return parameters; - }; + // Check condition parameters + for (const param of cond.parameters ?? []) { + if ( + typeof param === 'string' && + param.startsWith(CONTEXT_PARAM_PREFIX) + ) { + requestedParameters.add(param); + } + } + } + return requestedParameters; + } public async toJson(): Promise { const parameters = await this.toObj(); diff --git a/packages/shared/src/conditions/context/index.ts b/packages/taco/src/conditions/context/index.ts similarity index 100% rename from packages/shared/src/conditions/context/index.ts rename to packages/taco/src/conditions/context/index.ts diff --git a/packages/shared/src/conditions/context/providers.ts b/packages/taco/src/conditions/context/providers.ts similarity index 100% rename from packages/shared/src/conditions/context/providers.ts rename to packages/taco/src/conditions/context/providers.ts diff --git a/packages/shared/src/conditions/index.ts b/packages/taco/src/conditions/index.ts similarity index 76% rename from packages/shared/src/conditions/index.ts rename to packages/taco/src/conditions/index.ts index aab50c2b4..fad3849b1 100644 --- a/packages/shared/src/conditions/index.ts +++ b/packages/taco/src/conditions/index.ts @@ -1,8 +1,6 @@ -// TODO: Do we want structured exports in @nucypher/shared? import * as base from './base'; import * as predefined from './predefined'; -// TODO: Or do we want to export everything from the base and predefined modules? export * from './base'; export * from './predefined'; diff --git a/packages/shared/src/conditions/predefined/erc721.ts b/packages/taco/src/conditions/predefined/erc721.ts similarity index 90% rename from packages/shared/src/conditions/predefined/erc721.ts rename to packages/taco/src/conditions/predefined/erc721.ts index 1a81b8456..b85cc7833 100644 --- a/packages/shared/src/conditions/predefined/erc721.ts +++ b/packages/taco/src/conditions/predefined/erc721.ts @@ -1,5 +1,8 @@ -import { ContractCondition, ContractConditionProps } from '../base'; -import { ContractConditionType } from '../base/contract'; +import { + ContractCondition, + ContractConditionProps, + ContractConditionType, +} from '../base'; import { USER_ADDRESS_PARAM } from '../const'; // TODO: Rewrite these using Zod schemas? diff --git a/packages/shared/src/conditions/predefined/index.ts b/packages/taco/src/conditions/predefined/index.ts similarity index 100% rename from packages/shared/src/conditions/predefined/index.ts rename to packages/taco/src/conditions/predefined/index.ts diff --git a/packages/shared/src/conditions/zod.ts b/packages/taco/src/conditions/zod.ts similarity index 100% rename from packages/shared/src/conditions/zod.ts rename to packages/taco/src/conditions/zod.ts diff --git a/packages/taco/src/index.ts b/packages/taco/src/index.ts index ef26cc39a..b1c36b574 100644 --- a/packages/taco/src/index.ts +++ b/packages/taco/src/index.ts @@ -1,11 +1,7 @@ export { DkgPublicKey, ThresholdMessageKit } from '@nucypher/nucypher-core'; -export { - conditions, - fromBytes, - getPorterUri, - initialize, - toBytes, -} from '@nucypher/shared'; +export { fromBytes, getPorterUri, initialize, toBytes } from '@nucypher/shared'; + +export * as conditions from './conditions'; export { decrypt, encrypt } from './taco'; diff --git a/packages/taco/src/taco.ts b/packages/taco/src/taco.ts index 54dd79c2b..81c86ab63 100644 --- a/packages/taco/src/taco.ts +++ b/packages/taco/src/taco.ts @@ -5,8 +5,6 @@ import { ThresholdMessageKit, } from '@nucypher/nucypher-core'; import { - Condition, - ConditionExpression, DkgCoordinatorAgent, fromHexString, getPorterUri, @@ -15,6 +13,7 @@ import { import { ethers } from 'ethers'; import { keccak256 } from 'ethers/lib/utils'; +import { Condition, ConditionExpression } from './conditions'; import { DkgClient } from './dkg'; import { retrieveAndDecrypt } from './tdec'; @@ -69,9 +68,6 @@ export const encryptWithPublicKey = async ( } const conditionExpr = new ConditionExpression(condition); - // if (!authSigner) { - // authSigner = new Signer(SecretKey.random()); - // } const [ciphertext, authenticatedData] = encryptForDkg( message, diff --git a/packages/taco/src/tdec.ts b/packages/taco/src/tdec.ts index 139c38827..1c9c9dfe1 100644 --- a/packages/taco/src/tdec.ts +++ b/packages/taco/src/tdec.ts @@ -8,16 +8,12 @@ import { EncryptedThresholdDecryptionResponse, encryptForDkg, FerveoVariant, - SecretKey, SessionSharedSecret, SessionStaticSecret, - Signer, ThresholdDecryptionRequest, ThresholdMessageKit, } from '@nucypher/nucypher-core'; import { - ConditionContext, - ConditionExpression, DkgCoordinatorAgent, DkgParticipant, PorterClient, @@ -26,16 +22,14 @@ import { import { ethers } from 'ethers'; import { arrayify, keccak256 } from 'ethers/lib/utils'; -export const encryptMessageCbd = ( +import { ConditionContext, ConditionExpression } from './conditions'; + +export const encryptMessage = async ( plaintext: Uint8Array | string, encryptingKey: DkgPublicKey, conditions: ConditionExpression, - authorizationSigner?: Signer, -): ThresholdMessageKit => { - if (!authorizationSigner) { - authorizationSigner = new Signer(SecretKey.random()); - } - + authSigner: ethers.Signer, +): Promise => { const [ciphertext, authenticatedData] = encryptForDkg( plaintext instanceof Uint8Array ? plaintext : toBytes(plaintext), encryptingKey, @@ -43,10 +37,10 @@ export const encryptMessageCbd = ( ); const headerHash = keccak256(ciphertext.header.toBytes()); - const authorization = authorizationSigner.sign(arrayify(headerHash)); + const authorization = await authSigner.signMessage(arrayify(headerHash)); const acp = new AccessControlPolicy( authenticatedData, - authorization.toBEBytes(), + toBytes(authorization), ); return new ThresholdMessageKit(ciphertext, acp); diff --git a/packages/taco/src/web3.ts b/packages/taco/src/web3.ts new file mode 100644 index 000000000..f8923626b --- /dev/null +++ b/packages/taco/src/web3.ts @@ -0,0 +1,25 @@ +export interface Eip712TypedData { + types: { + Wallet: { name: string; type: string }[]; + }; + domain: { + salt: string; + chainId: number; + name: string; + version: string; + }; + message: { + blockHash: string; + address: string; + blockNumber: number; + signatureText: string; + }; +} + +export interface FormattedTypedData extends Eip712TypedData { + primaryType: 'Wallet'; + types: { + EIP712Domain: { name: string; type: string }[]; + Wallet: { name: string; type: string }[]; + }; +} diff --git a/packages/shared/test/conditions/base/condition.test.ts b/packages/taco/test/conditions/base/condition.test.ts similarity index 85% rename from packages/shared/test/conditions/base/condition.test.ts rename to packages/taco/test/conditions/base/condition.test.ts index d03226a12..9ff10c618 100644 --- a/packages/shared/test/conditions/base/condition.test.ts +++ b/packages/taco/test/conditions/base/condition.test.ts @@ -1,8 +1,4 @@ -import { - TEST_CHAIN_ID, - TEST_CONTRACT_ADDR, - testContractConditionObj, -} from '@nucypher/test-utils'; +import { TEST_CHAIN_ID, TEST_CONTRACT_ADDR } from '@nucypher/test-utils'; import { describe, expect, it } from 'vitest'; import { @@ -10,7 +6,8 @@ import { ContractCondition, ERC721Balance, ERC721Ownership, -} from '../../../src'; +} from '../../../src/conditions'; +import { testContractConditionObj } from '../../test-utils'; describe('validation', () => { const condition = new ERC721Balance({ diff --git a/packages/shared/test/conditions/base/contract.test.ts b/packages/taco/test/conditions/base/contract.test.ts similarity index 96% rename from packages/shared/test/conditions/base/contract.test.ts rename to packages/taco/test/conditions/base/contract.test.ts index 2feda5290..c4e875c7b 100644 --- a/packages/shared/test/conditions/base/contract.test.ts +++ b/packages/taco/test/conditions/base/contract.test.ts @@ -1,9 +1,5 @@ -import { - fakeProvider, - fakeSigner, - testContractConditionObj, - testFunctionAbi, -} from '@nucypher/test-utils'; +import { initialize } from '@nucypher/nucypher-core'; +import { fakeProvider, fakeSigner } from '@nucypher/test-utils'; import { beforeAll, describe, expect, it } from 'vitest'; import { @@ -11,13 +7,11 @@ import { ContractCondition, ContractConditionProps, CustomContextParam, - initialize, -} from '../../../src'; -import { - contractConditionSchema, FunctionAbiProps, -} from '../../../src/conditions/base/contract'; +} from '../../../src/conditions'; +import { contractConditionSchema } from '../../../src/conditions/base/contract'; import { USER_ADDRESS_PARAM } from '../../../src/conditions/const'; +import { testContractConditionObj, testFunctionAbi } from '../../test-utils'; describe('validation', () => { it('accepts on a valid schema', () => { diff --git a/packages/shared/test/conditions/base/rpc.test.ts b/packages/taco/test/conditions/base/rpc.test.ts similarity index 90% rename from packages/shared/test/conditions/base/rpc.test.ts rename to packages/taco/test/conditions/base/rpc.test.ts index 89d782184..c5fd7525f 100644 --- a/packages/shared/test/conditions/base/rpc.test.ts +++ b/packages/taco/test/conditions/base/rpc.test.ts @@ -1,8 +1,8 @@ -import { testRpcConditionObj } from '@nucypher/test-utils'; import { describe, expect, it } from 'vitest'; -import { RpcCondition } from '../../../src'; +import { RpcCondition } from '../../../src/conditions'; import { rpcConditionSchema } from '../../../src/conditions/base/rpc'; +import { testRpcConditionObj } from '../../test-utils'; describe('validation', () => { it('accepts on a valid schema', () => { diff --git a/packages/shared/test/conditions/base/time.test.ts b/packages/taco/test/conditions/base/time.test.ts similarity index 93% rename from packages/shared/test/conditions/base/time.test.ts rename to packages/taco/test/conditions/base/time.test.ts index e0b434551..330b11730 100644 --- a/packages/shared/test/conditions/base/time.test.ts +++ b/packages/taco/test/conditions/base/time.test.ts @@ -3,13 +3,11 @@ import { describe, expect, it } from 'vitest'; import { ReturnValueTestProps, TimeCondition, - TimeConditionProps, -} from '../../../src'; -import { TimeConditionMethod, - timeConditionSchema, + TimeConditionProps, TimeConditionType, -} from '../../../src/conditions/base/time'; +} from '../../../src/conditions'; +import { timeConditionSchema } from '../../../src/conditions/base/time'; describe('validation', () => { const returnValueTest: ReturnValueTestProps = { diff --git a/packages/shared/test/conditions/compound-condition.test.ts b/packages/taco/test/conditions/compound-condition.test.ts similarity index 94% rename from packages/shared/test/conditions/compound-condition.test.ts rename to packages/taco/test/conditions/compound-condition.test.ts index 8d5e8feea..b988b994a 100644 --- a/packages/shared/test/conditions/compound-condition.test.ts +++ b/packages/taco/test/conditions/compound-condition.test.ts @@ -1,15 +1,15 @@ -import { - testContractConditionObj, - testRpcConditionObj, - testTimeConditionObj, -} from '@nucypher/test-utils'; import { describe, expect, it } from 'vitest'; -import { CompoundCondition, Condition } from '../../src'; +import { CompoundCondition, Condition } from '../../src/conditions'; import { compoundConditionSchema, CompoundConditionType, } from '../../src/conditions/compound-condition'; +import { + testContractConditionObj, + testRpcConditionObj, + testTimeConditionObj, +} from '../test-utils'; describe('validation', () => { it('accepts or operator', () => { @@ -28,6 +28,7 @@ describe('validation', () => { it('accepts and operator', () => { const conditionObj = { + conditionType: CompoundConditionType, operator: 'and', operands: [testContractConditionObj, testTimeConditionObj], }; @@ -45,6 +46,7 @@ describe('validation', () => { it('rejects an invalid operator', () => { const result = CompoundCondition.validate(compoundConditionSchema, { + conditionType: CompoundConditionType, operator: 'not-an-operator', operands: [testRpcConditionObj, testTimeConditionObj], }); @@ -91,6 +93,7 @@ describe('validation', () => { it('accepts recursive compound conditions', () => { const conditionObj = { + conditionType: CompoundConditionType, operator: 'and', operands: [ testContractConditionObj, @@ -124,7 +127,7 @@ describe('validation', () => { }); const multichainCondition = { - conditionType: 'compound', + conditionType: CompoundConditionType, operator: 'and', operands: [1, 137, 5, 80001].map((chain) => ({ ...testRpcConditionObj, diff --git a/packages/shared/test/conditions/condition-expr.test.ts b/packages/taco/test/conditions/condition-expr.test.ts similarity index 93% rename from packages/shared/test/conditions/condition-expr.test.ts rename to packages/taco/test/conditions/condition-expr.test.ts index 101b1d024..4a24fc77c 100644 --- a/packages/shared/test/conditions/condition-expr.test.ts +++ b/packages/taco/test/conditions/condition-expr.test.ts @@ -1,12 +1,6 @@ -import { - TEST_CHAIN_ID, - TEST_CONTRACT_ADDR, - testContractConditionObj, - testFunctionAbi, - testReturnValueTest, - testRpcConditionObj, - testTimeConditionObj, -} from '@nucypher/test-utils'; +import { initialize } from '@nucypher/nucypher-core'; +import { objectEquals, toJSON } from '@nucypher/shared'; +import { TEST_CHAIN_ID, TEST_CONTRACT_ADDR } from '@nucypher/test-utils'; import { SemVer } from 'semver'; import { beforeAll, describe, expect, it } from 'vitest'; @@ -16,18 +10,22 @@ import { ContractCondition, ContractConditionProps, ERC721Balance, - initialize, - objectEquals, RpcCondition, RpcConditionType, TimeCondition, TimeConditionProps, - toJSON, -} from '../../src'; +} from '../../src/conditions'; import { USER_ADDRESS_PARAM } from '../../src/conditions/const'; +import { + testContractConditionObj, + testFunctionAbi, + testReturnValueTest, + testRpcConditionObj, + testTimeConditionObj, +} from '../test-utils'; describe('condition set', () => { - const erc721BalanceCondition = new ERC721Balance({ + const erc721Balance = new ERC721Balance({ chain: TEST_CHAIN_ID, contractAddress: TEST_CONTRACT_ADDR, }); @@ -76,7 +74,7 @@ describe('condition set', () => { it('same version and condition', () => { const conditionExprSameCurrentVersion = new ConditionExpression( rpcCondition, - ConditionExpression.VERSION, + ConditionExpression.version, ); expect( conditionExprCurrentVersion.equals(conditionExprSameCurrentVersion), @@ -141,7 +139,7 @@ describe('condition set', () => { }); it.each([ - erc721BalanceCondition, + erc721Balance, contractConditionNoAbi, contractConditionWithAbi, timeCondition, @@ -157,26 +155,21 @@ describe('condition set', () => { }); it('same contract condition although using erc721 helper', () => { - const erc721ConditionExpr = new ConditionExpression( - erc721BalanceCondition, - ); - const erc721ConditionData = erc721BalanceCondition.toObj(); + const conditionExpr = new ConditionExpression(erc721Balance); + const erc721ConditionData = erc721Balance.toObj(); const sameContractCondition = new ContractCondition(erc721ConditionData); const contractConditionExpr = new ConditionExpression( sameContractCondition, ); expect( - objectEquals( - erc721ConditionExpr.toObj(), - contractConditionExpr.toObj(), - ), + objectEquals(conditionExpr.toObj(), contractConditionExpr.toObj()), ).toBeTruthy(); }); }); describe('serialization / deserialization', () => { it.each([ - erc721BalanceCondition, + erc721Balance, contractConditionNoAbi, contractConditionWithAbi, rpcCondition, @@ -187,7 +180,7 @@ describe('condition set', () => { const conditionExprJson = conditionExpr.toJson(); expect(conditionExprJson).toBeDefined(); expect(conditionExprJson).toContain('version'); - expect(conditionExprJson).toContain(ConditionExpression.VERSION); + expect(conditionExprJson).toContain(ConditionExpression.version); expect(conditionExprJson).toContain('condition'); expect(conditionExprJson).toContain(toJSON(condition.toObj())); @@ -204,14 +197,14 @@ describe('condition set', () => { }); it('serializes to and from WASM conditions', () => { - const conditionExpr = new ConditionExpression(erc721BalanceCondition); + const conditionExpr = new ConditionExpression(erc721Balance); const wasmConditions = conditionExpr.toWASMConditions(); const fromWasm = ConditionExpression.fromWASMConditions(wasmConditions); expect(conditionExpr.equals(fromWasm)).toBeTruthy(); }); it('incompatible version', () => { - const currentVersion = new SemVer(ConditionExpression.VERSION); + const currentVersion = new SemVer(ConditionExpression.version); const invalidVersion = currentVersion.inc('major'); expect(() => { ConditionExpression.fromObj({ @@ -219,7 +212,7 @@ describe('condition set', () => { condition: testTimeConditionObj, }); }).toThrow( - `Version provided, ${invalidVersion}, is incompatible with current version, ${ConditionExpression.VERSION}`, + `Version provided, ${invalidVersion}, is incompatible with current version, ${ConditionExpression.version}`, ); }); @@ -244,7 +237,7 @@ describe('condition set', () => { } as unknown as TimeConditionProps; expect(() => { ConditionExpression.fromObj({ - version: ConditionExpression.VERSION, + version: ConditionExpression.version, condition: conditionObj, }); }).toThrow(`Invalid conditionType: ${invalidConditionType}`); @@ -258,28 +251,26 @@ describe('condition set', () => { } as unknown as TimeConditionProps; expect(() => { ConditionExpression.fromObj({ - version: ConditionExpression.VERSION, + version: ConditionExpression.version, condition: conditionObj, }); }).toThrow(/^Invalid condition/); }); it('erc721 condition serialization', () => { - const conditionExpr = new ConditionExpression(erc721BalanceCondition); + const conditionExpr = new ConditionExpression(erc721Balance); - const erc721BalanceConditionObj = erc721BalanceCondition.toObj(); + const asObj = erc721Balance.toObj(); const conditionExprJson = conditionExpr.toJson(); expect(conditionExprJson).toBeDefined(); expect(conditionExprJson).toContain('chain'); expect(conditionExprJson).toContain(TEST_CHAIN_ID.toString()); expect(conditionExprJson).toContain('contractAddress'); - expect(conditionExprJson).toContain( - erc721BalanceConditionObj.contractAddress, - ); + expect(conditionExprJson).toContain(asObj.contractAddress); expect(conditionExprJson).toContain('standardContractType'); expect(conditionExprJson).toContain('ERC721'); expect(conditionExprJson).toContain('method'); - expect(conditionExprJson).toContain(erc721BalanceConditionObj.method); + expect(conditionExprJson).toContain(asObj.method); expect(conditionExprJson).toContain('returnValueTest'); expect(conditionExprJson).not.toContain('functionAbi'); diff --git a/packages/shared/test/conditions/context.test.ts b/packages/taco/test/conditions/context.test.ts similarity index 97% rename from packages/shared/test/conditions/context.test.ts rename to packages/taco/test/conditions/context.test.ts index b8ed8084a..de035f61f 100644 --- a/packages/shared/test/conditions/context.test.ts +++ b/packages/taco/test/conditions/context.test.ts @@ -1,11 +1,5 @@ -import { - fakeProvider, - fakeSigner, - testContractConditionObj, - testFunctionAbi, - testReturnValueTest, - testRpcConditionObj, -} from '@nucypher/test-utils'; +import { initialize } from '@nucypher/nucypher-core'; +import { fakeProvider, fakeSigner } from '@nucypher/test-utils'; import { ethers } from 'ethers'; import { beforeAll, describe, expect, it } from 'vitest'; @@ -13,11 +7,16 @@ import { ConditionExpression, ContractCondition, CustomContextParam, - initialize, RpcCondition, -} from '../../src'; +} from '../../src/conditions'; import { USER_ADDRESS_PARAM } from '../../src/conditions/const'; import { RESERVED_CONTEXT_PARAMS } from '../../src/conditions/context/context'; +import { + testContractConditionObj, + testFunctionAbi, + testReturnValueTest, + testRpcConditionObj, +} from '../test-utils'; describe('context', () => { let provider: ethers.providers.Provider; diff --git a/packages/taco/test/dkg-client.test.ts b/packages/taco/test/dkg-client.test.ts index 035e09d88..ca1ac544b 100644 --- a/packages/taco/test/dkg-client.test.ts +++ b/packages/taco/test/dkg-client.test.ts @@ -1,10 +1,15 @@ import { DkgCoordinatorAgent } from '@nucypher/shared'; -import { fakeProvider, mockGetParticipants } from '@nucypher/test-utils'; +import { fakeProvider } from '@nucypher/test-utils'; import { beforeAll, describe, expect, it } from 'vitest'; import { initialize } from '../src'; -import { fakeRitualId, mockDkgParticipants, mockGetRitual } from './test-utils'; +import { + fakeRitualId, + mockDkgParticipants, + mockGetParticipants, + mockGetRitual, +} from './test-utils'; describe('DkgCoordinatorAgent', () => { beforeAll(async () => { @@ -22,7 +27,7 @@ describe('DkgCoordinatorAgent', () => { it('fetches participants from the coordinator', async () => { const provider = fakeProvider(); const getParticipantsSpy = mockGetParticipants( - mockDkgParticipants(fakeRitualId).participants, + (await mockDkgParticipants(fakeRitualId)).participants, ); const participants = await DkgCoordinatorAgent.getParticipants( provider, diff --git a/packages/taco/test/taco.test.ts b/packages/taco/test/taco.test.ts index 468c719b5..e33fcd7a0 100644 --- a/packages/taco/test/taco.test.ts +++ b/packages/taco/test/taco.test.ts @@ -7,7 +7,6 @@ import { fakeSigner, fakeTDecFlow, mockCbdDecrypt, - mockGetParticipants, mockGetRitualIdFromPublicKey, } from '@nucypher/test-utils'; import { expect, test } from 'vitest'; @@ -19,6 +18,7 @@ import { fakeDkgRitual, mockDkgParticipants, mockGetFinalizedRitualSpy, + mockGetParticipants, mockMakeSessionKey, } from './test-utils'; @@ -43,6 +43,7 @@ test('taco', () => { message, ownsNFT, mockedDkg.ritualId, + signer, ); expect(getFinalizedRitualSpy).toHaveBeenCalled(); @@ -52,7 +53,7 @@ test('taco', () => { dkgPublicKey: mockedDkg.dkg.publicKey(), thresholdMessageKit: messageKit, }); - const { participantSecrets, participants } = mockDkgParticipants( + const { participantSecrets, participants } = await mockDkgParticipants( mockedDkg.ritualId, ); const requesterSessionKey = SessionStaticSecret.random(); diff --git a/packages/taco/test/test-utils.ts b/packages/taco/test/test-utils.ts index 742e9bb99..2931ab66a 100644 --- a/packages/taco/test/test-utils.ts +++ b/packages/taco/test/test-utils.ts @@ -17,7 +17,7 @@ import { ValidatorMessage, } from '@nucypher/nucypher-core'; import { - ConditionExpression, + CoordinatorRitual, DkgCoordinatorAgent, DkgParticipant, DkgRitualState, @@ -26,15 +26,31 @@ import { zip, } from '@nucypher/shared'; import { - fakeConditionExpr, fakeDkgFlow, + fakeSigner, fakeTDecFlow, + TEST_CHAIN_ID, + TEST_CONTRACT_ADDR, } from '@nucypher/test-utils'; +import { ethers } from 'ethers'; import { keccak256 } from 'ethers/lib/utils'; import { SpyInstance, vi } from 'vitest'; +import { + ConditionExpression, + ContractConditionProps, + ContractConditionType, + ERC721Balance, + FunctionAbiProps, + ReturnValueTestProps, + RpcConditionProps, + RpcConditionType, + TimeConditionMethod, + TimeConditionProps, + TimeConditionType, +} from '../src/conditions'; import { DkgClient, DkgRitual } from '../src/dkg'; -import { encryptMessageCbd } from '../src/tdec'; +import { encryptMessage } from '../src/tdec'; export const fakeDkgTDecFlowE2E: ( ritualId?: number, @@ -43,7 +59,7 @@ export const fakeDkgTDecFlowE2E: ( message?: Uint8Array, sharesNum?: number, threshold?: number, -) => { +) => Promise<{ dkg: Dkg; serverAggregate: AggregatedTranscript; sharesNum: number; @@ -56,7 +72,7 @@ export const fakeDkgTDecFlowE2E: ( message: Uint8Array; thresholdMessageKit: ThresholdMessageKit; decryptionShares: DecryptionShareSimple[]; -} = ( +}> = async ( ritualId = 0, variant: FerveoVariant = FerveoVariant.precomputed, conditionExpr: ConditionExpression = fakeConditionExpr(), @@ -66,10 +82,11 @@ export const fakeDkgTDecFlowE2E: ( ) => { const ritual = fakeDkgFlow(variant, ritualId, sharesNum, threshold); const dkgPublicKey = ritual.dkg.publicKey(); - const thresholdMessageKit = encryptMessageCbd( + const thresholdMessageKit = await encryptMessage( message, dkgPublicKey, conditionExpr, + fakeSigner(), ); const { decryptionShares } = fakeTDecFlow({ @@ -87,22 +104,10 @@ export const fakeDkgTDecFlowE2E: ( }; }; -export const fakeCoordinatorRitual = ( +export const fakeCoordinatorRitual = async ( ritualId: number, -): { - 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 = fakeDkgTDecFlowE2E(); +): Promise => { + const ritual = await fakeDkgTDecFlowE2E(); const dkgPkBytes = ritual.dkg.publicKey().toBytes(); return { id: ritualId, @@ -122,13 +127,13 @@ export const fakeCoordinatorRitual = ( }; }; -export const mockDkgParticipants = ( +export const mockDkgParticipants = async ( ritualId: number, -): { +): Promise<{ participants: DkgParticipant[]; participantSecrets: Record; -} => { - const ritual = fakeDkgTDecFlowE2E(ritualId); +}> => { + const ritual = await fakeDkgTDecFlowE2E(ritualId); const label = toBytes(`${ritualId}`); const participantSecrets: Record = @@ -169,15 +174,14 @@ export const fakeDkgRitual = (ritual: { ); }; -export const mockGetRitual = (dkgRitual?: DkgRitual): SpyInstance => { - const { dkg, threshold, sharesNum } = fakeDkgTDecFlowE2E(); +export const mockGetRitual = (): SpyInstance => { return vi .spyOn(DkgCoordinatorAgent, 'getRitual') - .mockImplementation(async () => { - return Promise.resolve( - dkgRitual ?? fakeDkgRitual({ dkg, threshold, sharesNum }), - ); - }); + .mockImplementation( + (_provider: ethers.providers.Provider, _ritualId: number) => { + return Promise.resolve(fakeCoordinatorRitual(fakeRitualId)); + }, + ); }; export const mockGetFinalizedRitualSpy = ( @@ -193,3 +197,81 @@ export const mockMakeSessionKey = (secret: SessionStaticSecret) => { .spyOn(SessionStaticSecret, 'random') .mockImplementation(() => secret); }; + +export const testReturnValueTest: ReturnValueTestProps = { + index: 0, + comparator: '>', + value: '100', +}; + +export const testTimeConditionObj: TimeConditionProps = { + conditionType: TimeConditionType, + returnValueTest: { + index: 0, + comparator: '>', + value: '100', + }, + method: TimeConditionMethod, + chain: 5, +}; + +export const testRpcConditionObj: RpcConditionProps = { + conditionType: RpcConditionType, + chain: TEST_CHAIN_ID, + method: 'eth_getBalance', + parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', 'latest'], + returnValueTest: testReturnValueTest, +}; + +export const testContractConditionObj: ContractConditionProps = { + conditionType: ContractConditionType, + contractAddress: '0x0000000000000000000000000000000000000000', + chain: 5, + standardContractType: 'ERC20', + method: 'balanceOf', + parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77'], + returnValueTest: testReturnValueTest, +}; + +export const testFunctionAbi: FunctionAbiProps = { + name: 'myFunction', + type: 'function', + stateMutability: 'view', + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + { + internalType: 'uint256', + name: 'myCustomParam', + type: 'uint256', + }, + ], + outputs: [ + { + internalType: 'uint256', + name: 'someValue', + type: 'uint256', + }, + ], +}; + +export const fakeConditionExpr = () => { + const condition = new ERC721Balance({ + chain: TEST_CHAIN_ID, + contractAddress: TEST_CONTRACT_ADDR, + }); + return new ConditionExpression(condition); +}; + +export const mockGetParticipants = ( + participants: DkgParticipant[], +): SpyInstance => { + return vi + .spyOn(DkgCoordinatorAgent, 'getParticipants') + .mockImplementation(() => { + return Promise.resolve(participants); + }); +}; diff --git a/packages/test-utils/src/utils.ts b/packages/test-utils/src/utils.ts index 839756d00..9c139fc6e 100644 --- a/packages/test-utils/src/utils.ts +++ b/packages/test-utils/src/utils.ts @@ -2,6 +2,7 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unused-vars */ + import { AggregatedTranscript, Capsule, @@ -15,6 +16,7 @@ import { EthereumAddress, FerveoVariant, Keypair, + MessageKit, reencrypt, SecretKey, SessionStaticKey, @@ -29,11 +31,7 @@ import { import { CbdDecryptResult, ChecksumAddress, - Cohort, - ConditionExpression, DkgCoordinatorAgent, - DkgParticipant, - ERC721Balance, GetUrsulasResult, PorterClient, RetrieveCFragsResult, @@ -41,13 +39,10 @@ import { Ursula, zip, } from '@nucypher/shared'; -import { MessageKit } from '@nucypher/shared/dist/es'; import axios from 'axios'; import { ethers, providers, Wallet } from 'ethers'; import { expect, SpyInstance, vi } from 'vitest'; -import { TEST_CHAIN_ID, TEST_CONTRACT_ADDR } from './variables'; - export const bytesEqual = (first: Uint8Array, second: Uint8Array): boolean => first.length === second.length && first.every((value, index) => value === second[index]); @@ -284,24 +279,6 @@ export const fakeTDecFlow = ({ }; }; -export const fakeConditionExpr = () => { - const erc721Balance = new ERC721Balance({ - chain: TEST_CHAIN_ID, - contractAddress: TEST_CONTRACT_ADDR, - }); - return new ConditionExpression(erc721Balance); -}; - -export const mockGetParticipants = ( - participants: DkgParticipant[], -): SpyInstance => { - return vi - .spyOn(DkgCoordinatorAgent, 'getParticipants') - .mockImplementation(() => { - return Promise.resolve(participants); - }); -}; - export const mockCbdDecrypt = ( ritualId: number, decryptionShares: (DecryptionSharePrecomputed | DecryptionShareSimple)[], @@ -342,15 +319,6 @@ export const mockGetRitualIdFromPublicKey = (ritualId: number): SpyInstance => { }); }; -export const makeCohort = async (ursulas: Ursula[] = fakeUrsulas()) => { - const getUrsulasSpy = mockGetUrsulas(ursulas); - const porterUri = 'https://_this.should.crash'; - const numUrsulas = ursulas.length; - const cohort = await Cohort.create(porterUri, numUrsulas); - expect(getUrsulasSpy).toHaveBeenCalled(); - return cohort; -}; - export const mockRetrieveAndDecrypt = ( makeTreasureMapSpy: SpyInstance, encryptedMessageKit: MessageKit, diff --git a/packages/test-utils/src/variables.ts b/packages/test-utils/src/variables.ts index d15909dcb..3019c09bc 100644 --- a/packages/test-utils/src/variables.ts +++ b/packages/test-utils/src/variables.ts @@ -1,15 +1,3 @@ -import { - ContractConditionProps, - ContractConditionType, - FunctionAbiProps, - ReturnValueTestProps, - RpcConditionProps, - RpcConditionType, - TimeConditionMethod, - TimeConditionProps, - TimeConditionType, -} from '@nucypher/shared'; - export const aliceSecretKeyBytes = new Uint8Array([ 55, 82, 190, 189, 203, 164, 60, 148, 36, 86, 46, 123, 63, 152, 215, 113, 174, 86, 244, 44, 23, 227, 197, 68, 5, 85, 116, 31, 208, 152, 88, 53, @@ -24,63 +12,3 @@ export const TEST_CONTRACT_ADDR = '0x0000000000000000000000000000000000000001'; export const TEST_CONTRACT_ADDR_2 = '0x0000000000000000000000000000000000000002'; export const TEST_CHAIN_ID = 5; - -export const testReturnValueTest: ReturnValueTestProps = { - index: 0, - comparator: '>', - value: '100', -}; - -export const testTimeConditionObj: TimeConditionProps = { - conditionType: TimeConditionType, - returnValueTest: { - index: 0, - comparator: '>', - value: '100', - }, - method: TimeConditionMethod, - chain: 5, -}; - -export const testRpcConditionObj: RpcConditionProps = { - conditionType: RpcConditionType, - chain: TEST_CHAIN_ID, - method: 'eth_getBalance', - parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', 'latest'], - returnValueTest: testReturnValueTest, -}; - -export const testContractConditionObj: ContractConditionProps = { - conditionType: ContractConditionType, - contractAddress: '0x0000000000000000000000000000000000000000', - chain: 5, - standardContractType: 'ERC20', - method: 'balanceOf', - parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77'], - returnValueTest: testReturnValueTest, -}; - -export const testFunctionAbi: FunctionAbiProps = { - name: 'myFunction', - type: 'function', - stateMutability: 'view', - inputs: [ - { - internalType: 'address', - name: 'account', - type: 'address', - }, - { - internalType: 'uint256', - name: 'myCustomParam', - type: 'uint256', - }, - ], - outputs: [ - { - internalType: 'uint256', - name: 'someValue', - type: 'uint256', - }, - ], -}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fbae5123b..9f0e95c58 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -469,9 +469,6 @@ importers: '@ethersproject/abi': specifier: ^5.7.0 version: 5.7.0 - '@ethersproject/abstract-signer': - specifier: ^5.7.0 - version: 5.7.0 '@ethersproject/providers': specifier: ^5.7.2 version: 5.7.2 @@ -490,12 +487,6 @@ importers: qs: specifier: ^6.10.1 version: 6.11.2 - semver: - specifier: ^7.5.2 - version: 7.5.4 - zod: - specifier: ^3.22.1 - version: 3.22.2 devDependencies: '@nucypher/test-utils': specifier: workspace:* @@ -509,9 +500,6 @@ importers: '@types/qs': specifier: ^6.9.7 version: 6.9.8 - '@types/semver': - specifier: ^7.5.0 - version: 7.5.1 cz-conventional-changelog: specifier: ^3.0.1 version: 3.3.0 @@ -527,6 +515,9 @@ importers: packages/taco: dependencies: + '@ethersproject/abstract-signer': + specifier: ^5.7.0 + version: 5.7.0 '@nucypher/nucypher-core': specifier: 0.13.0-alpha.1 version: 0.13.0-alpha.1 @@ -536,10 +527,19 @@ importers: ethers: specifier: ^5.7.2 version: 5.7.2 + semver: + specifier: ^7.5.2 + version: 7.5.4 + zod: + specifier: ^3.22.1 + version: 3.22.2 devDependencies: '@nucypher/test-utils': specifier: workspace:* version: link:../test-utils + '@types/semver': + specifier: ^7.5.0 + version: 7.5.2 packages/test-utils: dependencies: @@ -3927,10 +3927,6 @@ packages: /@types/scheduler@0.16.3: resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} - /@types/semver@7.5.1: - resolution: {integrity: sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==} - dev: true - /@types/semver@7.5.2: resolution: {integrity: sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==} dev: true