From cb258e444cd8ff7dd525c38ee52194f71341d2a0 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Sun, 3 Sep 2023 17:44:01 +0200
Subject: [PATCH 1/5] apply pr suggestions for #273
---
src/characters/cbd-recipient.ts | 70 +++++--------------
src/characters/enrico.ts | 33 +++++----
test/integration/dkg-client.test.ts | 2 +-
test/unit/cbd-strategy.test.ts | 23 +++----
test/utils.ts | 100 ++++++++++++++++++----------
5 files changed, 112 insertions(+), 116 deletions(-)
diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts
index b7cba3aaa..97354ff49 100644
--- a/src/characters/cbd-recipient.ts
+++ b/src/characters/cbd-recipient.ts
@@ -1,26 +1,22 @@
import {
- AccessControlPolicy,
- AuthenticatedData,
- Ciphertext,
combineDecryptionSharesSimple,
Context,
DecryptionShareSimple,
- decryptWithSharedSecret,
EncryptedThresholdDecryptionRequest,
EncryptedThresholdDecryptionResponse,
FerveoVariant,
SessionSharedSecret,
SessionStaticSecret,
ThresholdDecryptionRequest,
+ ThresholdMessageKit,
} from '@nucypher/nucypher-core';
import { ethers } from 'ethers';
-import { keccak256 } from 'ethers/lib/utils';
import { DkgCoordinatorAgent, DkgParticipant } from '../agents/coordinator';
import { ConditionExpression } from '../conditions';
-import { DkgClient, DkgRitual } from '../dkg';
+import { DkgRitual } from '../dkg';
import { PorterClient } from '../porter';
-import { fromJSON, toBytes, toJSON } from '../utils';
+import { fromJSON, objectEquals, toJSON } from '../utils';
export type ThresholdDecrypterJSON = {
porterUri: string;
@@ -46,55 +42,25 @@ export class ThresholdDecrypter {
// Retrieve and decrypt ciphertext using provider and condition expression
public async retrieveAndDecrypt(
provider: ethers.providers.Provider,
- signer: ethers.Signer,
conditionExpr: ConditionExpression,
- ciphertext: Ciphertext
+ thresholdMessageKit: ThresholdMessageKit,
+ signer?: ethers.Signer
): Promise {
- const acp = await this.makeAcp(provider, signer, conditionExpr, ciphertext);
-
const decryptionShares = await this.retrieve(
provider,
conditionExpr,
- ciphertext,
- acp,
+ thresholdMessageKit,
signer
);
-
const sharedSecret = combineDecryptionSharesSimple(decryptionShares);
- return decryptWithSharedSecret(
- ciphertext,
- conditionExpr.asAad(),
- sharedSecret
- );
- }
-
- private async makeAcp(
- provider: ethers.providers.Provider,
- signer: ethers.Signer,
- conditionExpr: ConditionExpression,
- ciphertext: Ciphertext
- ) {
- const dkgRitual = await DkgClient.getExistingRitual(
- provider,
- this.ritualId
- );
- const authData = new AuthenticatedData(
- dkgRitual.dkgPublicKey,
- conditionExpr.toWASMConditions()
- );
-
- const headerHash = keccak256(ciphertext.header.toBytes());
- const authorization = await signer.signMessage(headerHash);
-
- return new AccessControlPolicy(authData, toBytes(authorization));
+ return thresholdMessageKit.decryptWithSharedSecret(sharedSecret);
}
// Retrieve decryption shares
public async retrieve(
provider: ethers.providers.Provider,
conditionExpr: ConditionExpression,
- ciphertext: Ciphertext,
- acp: AccessControlPolicy,
+ thresholdMessageKit: ThresholdMessageKit,
signer?: ethers.Signer
): Promise {
const dkgParticipants = await DkgCoordinatorAgent.getParticipants(
@@ -106,10 +72,9 @@ export class ThresholdDecrypter {
.toJson();
const { sharedSecrets, encryptedRequests } = this.makeDecryptionRequests(
this.ritualId,
- ciphertext,
- contextStr,
+ new Context(contextStr),
dkgParticipants,
- acp
+ thresholdMessageKit
);
const { encryptedResponses, errors } = await this.porter.cbdDecrypt(
@@ -154,10 +119,9 @@ export class ThresholdDecrypter {
private makeDecryptionRequests(
ritualId: number,
- ciphertext: Ciphertext,
- contextStr: string,
+ conditionContext: Context,
dkgParticipants: Array,
- acp: AccessControlPolicy
+ thresholdMessageKit: ThresholdMessageKit
): {
sharedSecrets: Record;
encryptedRequests: Record;
@@ -165,9 +129,9 @@ export class ThresholdDecrypter {
const decryptionRequest = new ThresholdDecryptionRequest(
ritualId,
FerveoVariant.simple,
- ciphertext.header,
- acp,
- new Context(contextStr)
+ thresholdMessageKit.ciphertextHeader,
+ thresholdMessageKit.acp,
+ conditionContext
);
const ephemeralSessionKey = this.makeSessionKey();
@@ -234,8 +198,6 @@ export class ThresholdDecrypter {
}
public equals(other: ThresholdDecrypter): boolean {
- return (
- this.porter.porterUrl.toString() === other.porter.porterUrl.toString()
- );
+ return objectEquals(this.toObj(), other.toObj());
}
}
diff --git a/src/characters/enrico.ts b/src/characters/enrico.ts
index 17c6ff358..3185ca050 100644
--- a/src/characters/enrico.ts
+++ b/src/characters/enrico.ts
@@ -1,11 +1,13 @@
import {
- Ciphertext,
+ AccessControlPolicy,
DkgPublicKey,
- ferveoEncrypt,
+ encryptForDkg,
MessageKit,
PublicKey,
SecretKey,
+ ThresholdMessageKit,
} from '@nucypher/nucypher-core';
+import { arrayify, keccak256 } from 'ethers/lib/utils';
import { ConditionExpression } from '../conditions';
import { Keyring } from '../keyring';
@@ -51,13 +53,13 @@ export class Enrico {
public encryptMessageCbd(
plaintext: Uint8Array | string,
- withConditions?: ConditionExpression
- ): { ciphertext: Ciphertext; aad: Uint8Array } {
- if (!withConditions) {
- withConditions = this.conditions;
+ conditions?: ConditionExpression
+ ): ThresholdMessageKit {
+ if (!conditions) {
+ conditions = this.conditions;
}
- if (!withConditions) {
+ if (!conditions) {
throw new Error('Conditions are required for CBD encryption.');
}
@@ -65,12 +67,19 @@ export class Enrico {
throw new Error('Wrong key type. Use encryptMessagePre instead.');
}
- const aad = withConditions.asAad();
- const ciphertext = ferveoEncrypt(
+ const [ciphertext, authenticatedData] = encryptForDkg(
plaintext instanceof Uint8Array ? plaintext : toBytes(plaintext),
- aad,
- this.encryptingKey
+ 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 { ciphertext, aad };
+
+ return new ThresholdMessageKit(ciphertext, acp);
}
}
diff --git a/test/integration/dkg-client.test.ts b/test/integration/dkg-client.test.ts
index c3eed8e8a..b2c1a6cc6 100644
--- a/test/integration/dkg-client.test.ts
+++ b/test/integration/dkg-client.test.ts
@@ -29,7 +29,7 @@ describe('DkgCoordinatorAgent', () => {
it('fetches participants from the coordinator', async () => {
const provider = fakeProvider(SecretKey.random().toBEBytes());
- const fakeParticipants = fakeDkgParticipants(fakeRitualId);
+ const fakeParticipants = await fakeDkgParticipants(fakeRitualId);
const getParticipantsSpy = mockGetParticipants(
fakeParticipants.participants
);
diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts
index 093dba61c..56fc7ddca 100644
--- a/test/unit/cbd-strategy.test.ts
+++ b/test/unit/cbd-strategy.test.ts
@@ -30,8 +30,8 @@ const {
// Shared test variables
const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes);
-const aliceSigner = fakeSigner(aliceSecretKey.toBEBytes());
const aliceProvider = fakeProvider(aliceSecretKey.toBEBytes());
+const aliceSigner = fakeSigner(aliceSecretKey.toBEBytes());
const ownsNFT = new ERC721Ownership({
contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77',
parameters: [3591],
@@ -54,10 +54,9 @@ async function makeDeployedCbdStrategy() {
const mockedDkg = fakeDkgFlow(variant, 0, 4, 4);
const mockedDkgRitual = fakeDkgRitual(mockedDkg);
- const web3Provider = fakeProvider(aliceSecretKey.toBEBytes());
const getUrsulasSpy = mockGetUrsulas(ursulas);
const getExistingRitualSpy = mockGetExistingRitual(mockedDkgRitual);
- const deployedStrategy = await strategy.deploy(web3Provider, ritualId);
+ const deployedStrategy = await strategy.deploy(aliceProvider, ritualId);
expect(getUrsulasSpy).toHaveBeenCalled();
expect(getExistingRitualSpy).toHaveBeenCalled();
@@ -104,20 +103,20 @@ describe('CbdDeployedStrategy', () => {
const { mockedDkg, deployedStrategy } = await makeDeployedCbdStrategy();
const message = 'this is a secret';
- const { ciphertext, aad } = deployedStrategy
+ const thresholdMessageKit = deployedStrategy
.makeEncrypter(conditionExpr)
.encryptMessageCbd(message);
// Setup mocks for `retrieveAndDecrypt`
- const { decryptionShares } = fakeTDecFlow({
+ const { decryptionShares } = await fakeTDecFlow({
...mockedDkg,
message: toBytes(message),
- aad,
- ciphertext,
+ conditionExpr,
+ dkgPublicKey: mockedDkg.dkg.publicKey(),
+ thresholdMessageKit,
});
- const { participantSecrets, participants } = fakeDkgParticipants(
- mockedDkg.ritualId,
- variant
+ const { participantSecrets, participants } = await fakeDkgParticipants(
+ mockedDkg.ritualId
);
const requesterSessionKey = SessionStaticSecret.random();
const decryptSpy = mockCbdDecrypt(
@@ -133,9 +132,9 @@ describe('CbdDeployedStrategy', () => {
const decryptedMessage =
await deployedStrategy.decrypter.retrieveAndDecrypt(
aliceProvider,
- aliceSigner,
conditionExpr,
- ciphertext
+ thresholdMessageKit,
+ aliceSigner
);
expect(getUrsulasSpy).toHaveBeenCalled();
expect(getParticipantsSpy).toHaveBeenCalled();
diff --git a/test/utils.ts b/test/utils.ts
index dad688591..7a815fc1b 100644
--- a/test/utils.ts
+++ b/test/utils.ts
@@ -7,16 +7,15 @@ import {
AggregatedTranscript,
Capsule,
CapsuleFrag,
- Ciphertext,
combineDecryptionSharesSimple,
DecryptionSharePrecomputed,
DecryptionShareSimple,
- decryptWithSharedSecret,
Dkg,
+ DkgPublicKey,
EncryptedThresholdDecryptionResponse,
EncryptedTreasureMap,
+ encryptForDkg,
EthereumAddress,
- ferveoEncrypt,
FerveoVariant,
Keypair,
PublicKey,
@@ -26,6 +25,7 @@ import {
SessionStaticKey,
SessionStaticSecret,
ThresholdDecryptionResponse,
+ ThresholdMessageKit,
Transcript,
Validator,
ValidatorMessage,
@@ -36,13 +36,15 @@ import axios from 'axios';
import { ethers, providers, Wallet } from 'ethers';
import { keccak256 } from 'ethers/lib/utils';
-import { Alice, Bob, Cohort, RemoteBob } from '../src';
+import { Alice, Bob, Cohort, Enrico, RemoteBob } from '../src';
import {
DkgCoordinatorAgent,
DkgParticipant,
DkgRitualState,
} from '../src/agents/coordinator';
import { ThresholdDecrypter } from '../src/characters/cbd-recipient';
+import { ConditionExpression } from '../src/conditions';
+import { ERC721Balance } from '../src/conditions/predefined';
import { DkgClient, DkgRitual } from '../src/dkg';
import { BlockchainPolicy, PreEnactedPolicy } from '../src/policies/policy';
import {
@@ -55,6 +57,8 @@ import {
import { ChecksumAddress } from '../src/types';
import { toBytes, toHexString, zip } from '../src/utils';
+import { TEST_CHAIN_ID, TEST_CONTRACT_ADDR } from './unit/testVariables';
+
export const bytesEqual = (first: Uint8Array, second: Uint8Array): boolean =>
first.length === second.length &&
first.every((value, index) => value === second[index]);
@@ -304,23 +308,31 @@ interface FakeDkgRitualFlow {
sharesNum: number;
threshold: number;
receivedMessages: ValidatorMessage[];
- ciphertext: Ciphertext;
- aad: Uint8Array;
dkg: Dkg;
message: Uint8Array;
+ dkgPublicKey: DkgPublicKey;
+ conditionExpr: ConditionExpression;
+ thresholdMessageKit: ThresholdMessageKit;
}
-export const fakeTDecFlow = ({
+export const fakeTDecFlow = async ({
validators,
validatorKeypairs,
ritualId,
sharesNum,
threshold,
receivedMessages,
- ciphertext,
- aad,
message,
+ conditionExpr,
+ dkgPublicKey,
+ thresholdMessageKit,
}: FakeDkgRitualFlow) => {
+ const [_ciphertext, authenticatedData] = encryptForDkg(
+ message,
+ dkgPublicKey,
+ conditionExpr.toWASMConditions()
+ );
+
// Having aggregated the transcripts, the validators can now create decryption shares
const decryptionShares: (
| DecryptionSharePrecomputed
@@ -336,56 +348,71 @@ export const fakeTDecFlow = ({
const decryptionShare = aggregate.createDecryptionShareSimple(
dkg,
- ciphertext.header,
- aad,
+ thresholdMessageKit.ciphertextHeader,
+ authenticatedData.aad(),
keypair
);
decryptionShares.push(decryptionShare);
});
- // Now, the decryption share can be used to decrypt the ciphertext
- // This part is in the client API
const sharedSecret = combineDecryptionSharesSimple(decryptionShares);
- // The client should have access to the public parameters of the DKG
- const plaintext = decryptWithSharedSecret(ciphertext, aad, sharedSecret);
+ const plaintext = thresholdMessageKit.decryptWithSharedSecret(sharedSecret);
if (!bytesEqual(plaintext, message)) {
throw new Error('Decryption failed');
}
- return { decryptionShares, sharedSecret, plaintext };
+ return {
+ authenticatedData,
+ decryptionShares,
+ plaintext,
+ sharedSecret,
+ thresholdMessageKit,
+ };
};
-export const fakeDkgTDecFlowE2e = (
- variant: FerveoVariant,
- message = toBytes('fake-message'),
- aad = toBytes('fake-aad'),
+const fakeConditionExpr = () => {
+ const erc721Balance = new ERC721Balance({
+ chain: TEST_CHAIN_ID,
+ contractAddress: TEST_CONTRACT_ADDR,
+ });
+ return new ConditionExpression(erc721Balance);
+};
+
+export const fakeDkgTDecFlowE2E = async (
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
+ );
- // In the meantime, the client creates a ciphertext and decryption request
- const ciphertext = ferveoEncrypt(message, aad, ritual.dkg.publicKey());
- const { decryptionShares } = fakeTDecFlow({
+ const { decryptionShares, authenticatedData } = await fakeTDecFlow({
...ritual,
- ciphertext,
- aad,
message,
+ conditionExpr,
+ dkgPublicKey,
+ thresholdMessageKit,
});
return {
...ritual,
message,
- aad,
- ciphertext,
decryptionShares,
+ authenticatedData,
+ thresholdMessageKit,
};
};
-export const fakeCoordinatorRitual = (
+export const fakeCoordinatorRitual = async (
ritualId: number
-): {
+): Promise<{
aggregationMismatch: boolean;
initTimestamp: number;
aggregatedTranscriptHash: string;
@@ -397,8 +424,8 @@ export const fakeCoordinatorRitual = (
aggregatedTranscript: string;
publicKeyHash: string;
totalAggregations: number;
-} => {
- const ritual = fakeDkgTDecFlowE2e(FerveoVariant.precomputed);
+}> => {
+ const ritual = await fakeDkgTDecFlowE2E();
const dkgPkBytes = ritual.dkg.publicKey().toBytes();
return {
id: ritualId,
@@ -418,14 +445,13 @@ export const fakeCoordinatorRitual = (
};
};
-export const fakeDkgParticipants = (
- ritualId: number,
- variant = FerveoVariant.precomputed
-): {
+export const fakeDkgParticipants = async (
+ ritualId: number
+): Promise<{
participants: DkgParticipant[];
participantSecrets: Record;
-} => {
- const ritual = fakeDkgTDecFlowE2e(variant);
+}> => {
+ const ritual = await fakeDkgTDecFlowE2E(ritualId);
const label = toBytes(`${ritualId}`);
const participantSecrets: Record =
From dcc741b78db143ee77d0868d644434b611581a4d Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Wed, 6 Sep 2023 10:30:43 +0200
Subject: [PATCH 2/5] apply pr suggestions
---
src/characters/cbd-recipient.ts | 36 ++++++++++-----------
src/conditions/condition-expr.ts | 12 +++----
src/conditions/condition.ts | 2 +-
src/conditions/context/context.ts | 35 ++++++++++++++++----
test/unit/cbd-strategy.test.ts | 1 -
test/unit/conditions/condition-expr.test.ts | 7 ++++
test/unit/conditions/context.test.ts | 4 ---
test/utils.ts | 15 ++-------
8 files changed, 62 insertions(+), 50 deletions(-)
diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts
index 97354ff49..9f9b11bd7 100644
--- a/src/characters/cbd-recipient.ts
+++ b/src/characters/cbd-recipient.ts
@@ -13,7 +13,7 @@ import {
import { ethers } from 'ethers';
import { DkgCoordinatorAgent, DkgParticipant } from '../agents/coordinator';
-import { ConditionExpression } from '../conditions';
+import { ConditionContext } from '../conditions';
import { DkgRitual } from '../dkg';
import { PorterClient } from '../porter';
import { fromJSON, objectEquals, toJSON } from '../utils';
@@ -42,13 +42,11 @@ export class ThresholdDecrypter {
// Retrieve and decrypt ciphertext using provider and condition expression
public async retrieveAndDecrypt(
provider: ethers.providers.Provider,
- conditionExpr: ConditionExpression,
thresholdMessageKit: ThresholdMessageKit,
signer?: ethers.Signer
): Promise {
const decryptionShares = await this.retrieve(
provider,
- conditionExpr,
thresholdMessageKit,
signer
);
@@ -59,7 +57,6 @@ export class ThresholdDecrypter {
// Retrieve decryption shares
public async retrieve(
provider: ethers.providers.Provider,
- conditionExpr: ConditionExpression,
thresholdMessageKit: ThresholdMessageKit,
signer?: ethers.Signer
): Promise {
@@ -67,15 +64,18 @@ export class ThresholdDecrypter {
provider,
this.ritualId
);
- const contextStr = await conditionExpr
- .buildContext(provider, {}, signer)
- .toJson();
- const { sharedSecrets, encryptedRequests } = this.makeDecryptionRequests(
- this.ritualId,
- new Context(contextStr),
- dkgParticipants,
- thresholdMessageKit
- );
+ const wasmContext = await ConditionContext.fromAccessControlPolicy(
+ provider,
+ thresholdMessageKit.acp,
+ signer
+ ).toWASMContext();
+ const { sharedSecrets, encryptedRequests } =
+ await this.makeDecryptionRequests(
+ this.ritualId,
+ wasmContext,
+ dkgParticipants,
+ thresholdMessageKit
+ );
const { encryptedResponses, errors } = await this.porter.cbdDecrypt(
encryptedRequests,
@@ -117,21 +117,21 @@ export class ThresholdDecrypter {
);
}
- private makeDecryptionRequests(
+ private async makeDecryptionRequests(
ritualId: number,
- conditionContext: Context,
+ wasmContext: Context,
dkgParticipants: Array,
thresholdMessageKit: ThresholdMessageKit
- ): {
+ ): Promise<{
sharedSecrets: Record;
encryptedRequests: Record;
- } {
+ }> {
const decryptionRequest = new ThresholdDecryptionRequest(
ritualId,
FerveoVariant.simple,
thresholdMessageKit.ciphertextHeader,
thresholdMessageKit.acp,
- conditionContext
+ wasmContext
);
const ephemeralSessionKey = this.makeSessionKey();
diff --git a/src/conditions/condition-expr.ts b/src/conditions/condition-expr.ts
index 697687088..ee12d7ccb 100644
--- a/src/conditions/condition-expr.ts
+++ b/src/conditions/condition-expr.ts
@@ -2,7 +2,7 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core';
import { ethers } from 'ethers';
import { SemVer } from 'semver';
-import { toBytes, toJSON } from '../utils';
+import { toJSON } from '../utils';
import { Condition } from './condition';
import { ConditionContext, CustomContextParam } from './context';
@@ -13,7 +13,7 @@ export type ConditionExpressionJSON = {
};
export class ConditionExpression {
- static VERSION = '1.0.0';
+ public static VERSION = '1.0.0';
constructor(
public readonly condition: Condition,
@@ -61,6 +61,10 @@ export class ConditionExpression {
return new WASMConditions(toJSON(this.toObj()));
}
+ public static fromWASMConditions(conditions: WASMConditions) {
+ return ConditionExpression.fromJSON(conditions.toString());
+ }
+
public buildContext(
provider: ethers.providers.Provider,
customParameters: Record = {},
@@ -78,10 +82,6 @@ export class ConditionExpression {
return this.condition.requiresSigner();
}
- public asAad(): Uint8Array {
- return toBytes(this.toJson());
- }
-
public equals(other: ConditionExpression): boolean {
return [
this.version === other.version,
diff --git a/src/conditions/condition.ts b/src/conditions/condition.ts
index b16444ef1..26f08618d 100644
--- a/src/conditions/condition.ts
+++ b/src/conditions/condition.ts
@@ -75,6 +75,6 @@ export class Condition {
}
public equals(other: Condition) {
- return objectEquals(this, other);
+ return objectEquals(this.toObj(), other.toObj());
}
}
diff --git a/src/conditions/context/context.ts b/src/conditions/context/context.ts
index b32e5a4e1..a71e90d91 100644
--- a/src/conditions/context/context.ts
+++ b/src/conditions/context/context.ts
@@ -1,8 +1,13 @@
-import { Conditions as WASMConditions } from '@nucypher/nucypher-core';
+import {
+ AccessControlPolicy,
+ Context,
+ Conditions as WASMConditions,
+} from '@nucypher/nucypher-core';
import { ethers } from 'ethers';
import { fromJSON, toJSON } from '../../utils';
import { Condition } from '../condition';
+import { ConditionExpression } from '../condition-expr';
import { USER_ADDRESS_PARAM } from '../const';
import { TypedSignature, WalletAuthenticationProvider } from './providers';
@@ -31,7 +36,7 @@ export class ConditionContext {
this.validate();
}
- public requiresSigner(): boolean {
+ private requiresSigner(): boolean {
return this.conditions.some((cond) => cond.requiresSigner());
}
@@ -120,19 +125,35 @@ export class ConditionContext {
return parameters;
};
- public toJson = async (): Promise => {
+ public async toJson(): Promise {
const parameters = await this.toObj();
return toJSON(parameters);
- };
+ }
- public withCustomParams = (
+ public withCustomParams(
params: Record
- ): ConditionContext => {
+ ): ConditionContext {
return new ConditionContext(
this.provider,
this.conditions,
params,
this.signer
);
- };
+ }
+
+ public async toWASMContext(): Promise {
+ const asJson = await this.toJson();
+ return new Context(asJson);
+ }
+
+ public static fromAccessControlPolicy(
+ provider: ethers.providers.Provider,
+ acp: AccessControlPolicy,
+ signer?: ethers.Signer
+ ): ConditionContext {
+ const conditions = acp.conditions
+ ? [ConditionExpression.fromWASMConditions(acp.conditions).condition]
+ : [];
+ return new ConditionContext(provider, conditions, {}, signer);
+ }
}
diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts
index 56fc7ddca..bbba1606f 100644
--- a/test/unit/cbd-strategy.test.ts
+++ b/test/unit/cbd-strategy.test.ts
@@ -132,7 +132,6 @@ describe('CbdDeployedStrategy', () => {
const decryptedMessage =
await deployedStrategy.decrypter.retrieveAndDecrypt(
aliceProvider,
- conditionExpr,
thresholdMessageKit,
aliceSigner
);
diff --git a/test/unit/conditions/condition-expr.test.ts b/test/unit/conditions/condition-expr.test.ts
index ff53730e1..f7d11fe2c 100644
--- a/test/unit/conditions/condition-expr.test.ts
+++ b/test/unit/conditions/condition-expr.test.ts
@@ -188,6 +188,13 @@ describe('condition set', () => {
expect(conditionExprFromJson.equals(conditionExprFromJson)).toBeTruthy();
});
+ it('serializes to and from WASM conditions', () => {
+ const conditionExpr = new ConditionExpression(erc721BalanceCondition);
+ const wasmConditions = conditionExpr.toWASMConditions();
+ const fromWasm = ConditionExpression.fromWASMConditions(wasmConditions);
+ expect(conditionExpr.equals(fromWasm)).toBeTruthy();
+ });
+
it('incompatible version', () => {
const currentVersion = new SemVer(ConditionExpression.VERSION);
const invalidVersion = currentVersion.inc('major');
diff --git a/test/unit/conditions/context.test.ts b/test/unit/conditions/context.test.ts
index 3f0f9e501..124fd3e07 100644
--- a/test/unit/conditions/context.test.ts
+++ b/test/unit/conditions/context.test.ts
@@ -90,20 +90,16 @@ describe('context parameters', () => {
};
const condition = new ContractCondition(conditionObj);
const conditionExpr = new ConditionExpression(condition);
- const conditionContext = conditionExpr.buildContext(provider, {}, signer);
expect(conditionExpr.contextRequiresSigner()).toBe(true);
- expect(conditionContext.requiresSigner()).toBe(true);
});
it('detects if a signer is not required', () => {
const condition = new RpcCondition(testRpcConditionObj);
const conditionExpr = new ConditionExpression(condition);
- const conditionContext = conditionExpr.buildContext(provider, {}, signer);
expect(JSON.stringify(condition.toObj()).includes(USER_ADDRESS_PARAM)).toBe(
false
);
expect(conditionExpr.contextRequiresSigner()).toBe(false);
- expect(conditionContext.requiresSigner()).toBe(false);
});
describe('custom method parameters', () => {
diff --git a/test/utils.ts b/test/utils.ts
index 7a815fc1b..b40e63ae9 100644
--- a/test/utils.ts
+++ b/test/utils.ts
@@ -14,7 +14,6 @@ import {
DkgPublicKey,
EncryptedThresholdDecryptionResponse,
EncryptedTreasureMap,
- encryptForDkg,
EthereumAddress,
FerveoVariant,
Keypair,
@@ -323,16 +322,8 @@ export const fakeTDecFlow = async ({
threshold,
receivedMessages,
message,
- conditionExpr,
- dkgPublicKey,
thresholdMessageKit,
}: FakeDkgRitualFlow) => {
- const [_ciphertext, authenticatedData] = encryptForDkg(
- message,
- dkgPublicKey,
- conditionExpr.toWASMConditions()
- );
-
// Having aggregated the transcripts, the validators can now create decryption shares
const decryptionShares: (
| DecryptionSharePrecomputed
@@ -349,7 +340,7 @@ export const fakeTDecFlow = async ({
const decryptionShare = aggregate.createDecryptionShareSimple(
dkg,
thresholdMessageKit.ciphertextHeader,
- authenticatedData.aad(),
+ thresholdMessageKit.acp.aad(),
keypair
);
decryptionShares.push(decryptionShare);
@@ -362,7 +353,6 @@ export const fakeTDecFlow = async ({
throw new Error('Decryption failed');
}
return {
- authenticatedData,
decryptionShares,
plaintext,
sharedSecret,
@@ -393,7 +383,7 @@ export const fakeDkgTDecFlowE2E = async (
conditionExpr
);
- const { decryptionShares, authenticatedData } = await fakeTDecFlow({
+ const { decryptionShares } = await fakeTDecFlow({
...ritual,
message,
conditionExpr,
@@ -405,7 +395,6 @@ export const fakeDkgTDecFlowE2E = async (
...ritual,
message,
decryptionShares,
- authenticatedData,
thresholdMessageKit,
};
};
From 3511348b3a9bd8d08d5599f50f42e10433075f57 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Wed, 6 Sep 2023 15:26:36 +0200
Subject: [PATCH 3/5] apply pr suggestions
---
test/unit/cbd-strategy.test.ts | 1 -
test/unit/conditions/context.test.ts | 16 ++++++++++++++++
test/utils.ts | 2 --
3 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts
index bbba1606f..7251262b1 100644
--- a/test/unit/cbd-strategy.test.ts
+++ b/test/unit/cbd-strategy.test.ts
@@ -111,7 +111,6 @@ describe('CbdDeployedStrategy', () => {
const { decryptionShares } = await fakeTDecFlow({
...mockedDkg,
message: toBytes(message),
- conditionExpr,
dkgPublicKey: mockedDkg.dkg.publicKey(),
thresholdMessageKit,
});
diff --git a/test/unit/conditions/context.test.ts b/test/unit/conditions/context.test.ts
index 124fd3e07..1cc3b19d5 100644
--- a/test/unit/conditions/context.test.ts
+++ b/test/unit/conditions/context.test.ts
@@ -102,6 +102,22 @@ describe('context parameters', () => {
expect(conditionExpr.contextRequiresSigner()).toBe(false);
});
+ it('rejects on a missing signer', () => {
+ const conditionObj = {
+ ...testContractConditionObj,
+ returnValueTest: {
+ ...testReturnValueTest,
+ value: USER_ADDRESS_PARAM,
+ },
+ };
+ const condition = new ContractCondition(conditionObj);
+ const conditionExpr = new ConditionExpression(condition);
+ expect(conditionExpr.contextRequiresSigner()).toBe(true);
+ expect(() => conditionExpr.buildContext(provider, {}, undefined)).toThrow(
+ `Cannot use ${USER_ADDRESS_PARAM} as custom parameter without a signer`
+ );
+ });
+
describe('custom method parameters', () => {
const contractConditionObj = {
...testContractConditionObj,
diff --git a/test/utils.ts b/test/utils.ts
index b40e63ae9..11f8b6714 100644
--- a/test/utils.ts
+++ b/test/utils.ts
@@ -310,7 +310,6 @@ interface FakeDkgRitualFlow {
dkg: Dkg;
message: Uint8Array;
dkgPublicKey: DkgPublicKey;
- conditionExpr: ConditionExpression;
thresholdMessageKit: ThresholdMessageKit;
}
@@ -386,7 +385,6 @@ export const fakeDkgTDecFlowE2E = async (
const { decryptionShares } = await fakeTDecFlow({
...ritual,
message,
- conditionExpr,
dkgPublicKey,
thresholdMessageKit,
});
From b11f1d48dbb78240417b65a33ddb3e9ca1994350 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Wed, 6 Sep 2023 15:57:45 +0200
Subject: [PATCH 4/5] update to nucypher-core@0.13.0
---
package.json | 2 +-
src/conditions/context/context.ts | 15 +++++++--------
yarn.lock | 8 ++++----
3 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/package.json b/package.json
index 76fade158..d9d8ceb6b 100644
--- a/package.json
+++ b/package.json
@@ -52,7 +52,7 @@
"prebuild": "yarn typechain"
},
"dependencies": {
- "@nucypher/nucypher-core": "^0.12.0",
+ "@nucypher/nucypher-core": "^0.13.0",
"axios": "^1.5.0",
"deep-equal": "^2.2.1",
"ethers": "^5.7.2",
diff --git a/src/conditions/context/context.ts b/src/conditions/context/context.ts
index a71e90d91..be4b72162 100644
--- a/src/conditions/context/context.ts
+++ b/src/conditions/context/context.ts
@@ -36,10 +36,6 @@ export class ConditionContext {
this.validate();
}
- private requiresSigner(): boolean {
- return this.conditions.some((cond) => cond.requiresSigner());
- }
-
private validate() {
Object.keys(this.customParameters).forEach((key) => {
if (RESERVED_CONTEXT_PARAMS.includes(key)) {
@@ -54,7 +50,10 @@ export class ConditionContext {
}
});
- if (this.requiresSigner() && !this.signer) {
+ const conditionRequiresSigner = this.conditions.some((c) =>
+ c.requiresSigner()
+ );
+ if (conditionRequiresSigner && !this.signer) {
throw new Error(
`Cannot use ${USER_ADDRESS_PARAM} as custom parameter without a signer`
);
@@ -151,9 +150,9 @@ export class ConditionContext {
acp: AccessControlPolicy,
signer?: ethers.Signer
): ConditionContext {
- const conditions = acp.conditions
- ? [ConditionExpression.fromWASMConditions(acp.conditions).condition]
- : [];
+ const conditions = [
+ ConditionExpression.fromWASMConditions(acp.conditions).condition,
+ ];
return new ConditionContext(provider, conditions, {}, signer);
}
}
diff --git a/yarn.lock b/yarn.lock
index 7a7ef1720..c495cb6b3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1818,10 +1818,10 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
-"@nucypher/nucypher-core@^0.12.0":
- version "0.12.0"
- resolved "https://registry.yarnpkg.com/@nucypher/nucypher-core/-/nucypher-core-0.12.0.tgz#0274026f3996601994e9639c06ab79c62c8f5d7c"
- integrity sha512-hEjjnTSLNqHUiIF6U02j+M8jUOBCOt5mGG78lMHoathbak15AmYX4gkdkHTg2Ssvt5o77Cwzsn5YgGM4Haf7AQ==
+"@nucypher/nucypher-core@^0.13.0":
+ version "0.13.0"
+ resolved "https://registry.yarnpkg.com/@nucypher/nucypher-core/-/nucypher-core-0.13.0.tgz#071263931d4e9604b428ea738ebe8ee5e1f302f3"
+ integrity sha512-HfEbrQvngOHIn0bMAdqPIF7WzkLdb5+sMhmX7bQl2SINABQ6FGGN8G+Arb+pbkYgru5qeQ+RTTbCBwBNaxFKEg==
"@sinclair/typebox@^0.27.8":
version "0.27.8"
From 363718fba7e9b20bc276c49162626ec921acd35d Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Fri, 8 Sep 2023 17:05:44 +0200
Subject: [PATCH 5/5] apply pr suggestions
---
src/characters/cbd-recipient.ts | 4 ++--
src/conditions/context/context.ts | 20 ++++++++------------
test/unit/conditions/context.test.ts | 2 +-
3 files changed, 11 insertions(+), 15 deletions(-)
diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts
index 9f9b11bd7..ad973616a 100644
--- a/src/characters/cbd-recipient.ts
+++ b/src/characters/cbd-recipient.ts
@@ -64,9 +64,9 @@ export class ThresholdDecrypter {
provider,
this.ritualId
);
- const wasmContext = await ConditionContext.fromAccessControlPolicy(
+ const wasmContext = await ConditionContext.fromConditions(
provider,
- thresholdMessageKit.acp,
+ thresholdMessageKit.acp.conditions,
signer
).toWASMContext();
const { sharedSecrets, encryptedRequests } =
diff --git a/src/conditions/context/context.ts b/src/conditions/context/context.ts
index be4b72162..c1d8426df 100644
--- a/src/conditions/context/context.ts
+++ b/src/conditions/context/context.ts
@@ -1,8 +1,4 @@
-import {
- AccessControlPolicy,
- Context,
- Conditions as WASMConditions,
-} from '@nucypher/nucypher-core';
+import { Context, Conditions as WASMConditions } from '@nucypher/nucypher-core';
import { ethers } from 'ethers';
import { fromJSON, toJSON } from '../../utils';
@@ -55,7 +51,7 @@ export class ConditionContext {
);
if (conditionRequiresSigner && !this.signer) {
throw new Error(
- `Cannot use ${USER_ADDRESS_PARAM} as custom parameter without a signer`
+ `Condition contains ${USER_ADDRESS_PARAM} context variable and requires a signer to populate`
);
}
@@ -96,7 +92,7 @@ export class ConditionContext {
if (requestedParameters.has(USER_ADDRESS_PARAM)) {
if (!this.walletAuthProvider) {
throw new Error(
- `Cannot use ${USER_ADDRESS_PARAM} as custom parameter without a signer`
+ `Condition contains ${USER_ADDRESS_PARAM} context variable and requires a signer to populate`
);
}
parameters[USER_ADDRESS_PARAM] =
@@ -145,14 +141,14 @@ export class ConditionContext {
return new Context(asJson);
}
- public static fromAccessControlPolicy(
+ public static fromConditions(
provider: ethers.providers.Provider,
- acp: AccessControlPolicy,
+ conditions: WASMConditions,
signer?: ethers.Signer
): ConditionContext {
- const conditions = [
- ConditionExpression.fromWASMConditions(acp.conditions).condition,
+ const innerConditions = [
+ ConditionExpression.fromWASMConditions(conditions).condition,
];
- return new ConditionContext(provider, conditions, {}, signer);
+ return new ConditionContext(provider, innerConditions, {}, signer);
}
}
diff --git a/test/unit/conditions/context.test.ts b/test/unit/conditions/context.test.ts
index 1cc3b19d5..4a0815912 100644
--- a/test/unit/conditions/context.test.ts
+++ b/test/unit/conditions/context.test.ts
@@ -114,7 +114,7 @@ describe('context parameters', () => {
const conditionExpr = new ConditionExpression(condition);
expect(conditionExpr.contextRequiresSigner()).toBe(true);
expect(() => conditionExpr.buildContext(provider, {}, undefined)).toThrow(
- `Cannot use ${USER_ADDRESS_PARAM} as custom parameter without a signer`
+ `Condition contains ${USER_ADDRESS_PARAM} context variable and requires a signer to populate`
);
});