Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement local ritual verification #261

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## [1.0.0-alpha.2](https://github.com/nucypher/nucypher-ts/compare/v1.0.0-alpha.1...v1.0.0-alpha.2) (2023-08-02)


### ⚠ BREAKING CHANGES

* porter uris are based on networks, not chain ids
* replaced configuration with raw porter uri

### Features

* implement local ritual verificaiton ([39241c0](https://github.com/nucypher/nucypher-ts/commit/39241c0cacc2b29bf11a5f4c88a6d53bb8ea4375))
* porter uris are based on networks, not chain ids ([d911481](https://github.com/nucypher/nucypher-ts/commit/d911481d0f1b8f80e022b2c32de5aaa53607a2f9))
* replaced configuration with raw porter uri ([fcd7fc0](https://github.com/nucypher/nucypher-ts/commit/fcd7fc0cb505b71a6e709ae37c1c53f9b2261f53))

## [1.0.0-alpha.1](https://github.com/nucypher/nucypher-ts/compare/v1.0.0-alpha.0...v1.0.0-alpha.1) (2023-07-10)


Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@nucypher/nucypher-ts",
"author": "Piotr Roslaniec <[email protected]>",
"version": "1.0.0-alpha.1",
"version": "1.0.0-alpha.2",
"license": "GPL-3.0-only",
"repository": {
"type": "git",
Expand Down Expand Up @@ -52,7 +52,7 @@
"prebuild": "yarn typechain"
},
"dependencies": {
"@nucypher/nucypher-core": "^0.10.0",
"@nucypher/nucypher-core": "^0.11.0",
"axios": "^0.21.1",
"deep-equal": "^2.2.1",
"ethers": "^5.4.1",
Expand Down
72 changes: 66 additions & 6 deletions src/agents/coordinator.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { SessionStaticKey } from '@nucypher/nucypher-core';
import { SessionStaticKey, Transcript } from '@nucypher/nucypher-core';
import { ethers } from 'ethers';

import {
Coordinator,
Coordinator__factory,
} from '../../types/ethers-contracts';
import { BLS12381 } from '../../types/ethers-contracts/Coordinator';
import { ChecksumAddress } from '../types';
import { fromHexString } from '../utils';

import { getContract } from './contracts';
import { DEFAULT_WAIT_N_CONFIRMATIONS, getContract } from './contracts';

export interface CoordinatorRitual {
initiator: string;
Expand All @@ -24,12 +25,22 @@ export interface CoordinatorRitual {
export type DkgParticipant = {
provider: string;
aggregated: boolean;
transcript: Transcript;
decryptionRequestStaticKey: SessionStaticKey;
};

export enum DkgRitualState {
NON_INITIATED,
AWAITING_TRANSCRIPTS,
AWAITING_AGGREGATIONS,
TIMEOUT,
INVALID,
FINALIZED,
}

export class DkgCoordinatorAgent {
public static async getParticipants(
provider: ethers.providers.Provider,
provider: ethers.providers.Web3Provider,
ritualId: number
): Promise<DkgParticipant[]> {
const Coordinator = await this.connectReadOnly(provider);
Expand All @@ -39,27 +50,76 @@ export class DkgCoordinatorAgent {
return {
provider: participant.provider,
aggregated: participant.aggregated,
transcript: Transcript.fromBytes(fromHexString(participant.transcript)),
decryptionRequestStaticKey: SessionStaticKey.fromBytes(
fromHexString(participant.decryptionRequestStaticKey)
),
};
});
}

public static async initializeRitual(
provider: ethers.providers.Web3Provider,
providers: ChecksumAddress[]
): Promise<number> {
const Coordinator = await this.connectReadWrite(provider);
const tx = await Coordinator.initiateRitual(providers);
const txReceipt = await tx.wait(DEFAULT_WAIT_N_CONFIRMATIONS);
const [ritualStartEvent] = txReceipt.events ?? [];
if (!ritualStartEvent) {
throw new Error('Ritual start event not found');
}
return ritualStartEvent.args?.ritualId;
}

public static async getRitual(
provider: ethers.providers.Provider,
provider: ethers.providers.Web3Provider,
ritualId: number
): Promise<CoordinatorRitual> {
const Coordinator = await this.connectReadOnly(provider);
return Coordinator.rituals(ritualId);
}

private static async connectReadOnly(provider: ethers.providers.Provider) {
public static async getRitualState(
provider: ethers.providers.Web3Provider,
ritualId: number
): Promise<DkgRitualState> {
const Coordinator = await this.connectReadOnly(provider);
return await Coordinator.getRitualState(ritualId);
}

public static async onRitualEndEvent(
provider: ethers.providers.Web3Provider,
ritualId: number,
callback: (successful: boolean) => void
): Promise<void> {
const Coordinator = await this.connectReadOnly(provider);
// We leave `initiator` undefined because we don't care who the initiator is
// We leave `successful` undefined because we don't care if the ritual was successful
const eventFilter = Coordinator.filters.EndRitual(
ritualId,
undefined,
undefined
);
Coordinator.once(eventFilter, (_ritualId, _initiator, successful) => {
callback(successful);
});
}

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

private static async connectReadWrite(
web3Provider: ethers.providers.Web3Provider
) {
return await this.connect(web3Provider, web3Provider.getSigner());
}

private static async connect(
provider: ethers.providers.Provider,
provider: ethers.providers.Web3Provider,
signer?: ethers.providers.JsonRpcSigner
): Promise<Coordinator> {
const network = await provider.getNetwork();
Expand Down
8 changes: 5 additions & 3 deletions src/agents/subscription-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class PreSubscriptionManagerAgent {
}

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

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

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

private static async connect(
provider: ethers.providers.Provider,
provider: ethers.providers.Web3Provider,
signer?: ethers.providers.JsonRpcSigner
): Promise<SubscriptionManager> {
const network = await provider.getNetwork();
Expand Down
43 changes: 20 additions & 23 deletions src/characters/alice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,22 @@ import {
} from '@nucypher/nucypher-core';
import { ethers } from 'ethers';

import { Configuration } from '../config';
import { Keyring } from '../keyring';
import {
BlockchainPolicy,
BlockchainPolicyParameters,
EnactedPolicy,
PreEnactedPolicy,
} from '../policies/policy';
import { PorterClient } from '../porter';
import { ChecksumAddress } from '../types';

import { RemoteBob } from './bob';
import { Porter } from './porter';

export class Alice {
private readonly porter: Porter;
private readonly keyring: Keyring;

private constructor(
config: Configuration,
secretKey: SecretKey,
public readonly web3Provider: ethers.providers.Web3Provider
) {
this.porter = new Porter(config.porterUri);
private constructor(secretKey: SecretKey) {
this.keyring = new Keyring(secretKey);
}

Expand All @@ -40,43 +33,45 @@ export class Alice {
return this.keyring.signer;
}

public static fromSecretKey(
config: Configuration,
secretKey: SecretKey,
web3Provider: ethers.providers.Web3Provider
): Alice {
return new Alice(config, secretKey, web3Provider);
public static fromSecretKey(secretKey: SecretKey): Alice {
return new Alice(secretKey);
}

public getPolicyEncryptingKeyFromLabel(label: string): PublicKey {
return this.keyring.getPublicKeyFromLabel(label);
}

public async grant(
web3Provider: ethers.providers.Web3Provider,
porterUri: string,
policyParameters: BlockchainPolicyParameters,
includeUrsulas?: readonly ChecksumAddress[],
excludeUrsulas?: readonly ChecksumAddress[]
): Promise<EnactedPolicy> {
const ursulas = await this.porter.getUrsulas(
const porter = new PorterClient(porterUri);
const ursulas = await porter.getUrsulas(
policyParameters.shares,
excludeUrsulas,
includeUrsulas
);
const policy = await this.createPolicy(policyParameters);
return await policy.enact(ursulas);
const policy = await this.createPolicy(web3Provider, policyParameters);
return await policy.enact(web3Provider, ursulas);
}

public async generatePreEnactedPolicy(
web3Provider: ethers.providers.Web3Provider,
porterUri: string,
policyParameters: BlockchainPolicyParameters,
includeUrsulas?: readonly ChecksumAddress[],
excludeUrsulas?: readonly ChecksumAddress[]
): Promise<PreEnactedPolicy> {
const ursulas = await this.porter.getUrsulas(
const porter = new PorterClient(porterUri);
const ursulas = await porter.getUrsulas(
policyParameters.shares,
excludeUrsulas,
includeUrsulas
);
const policy = await this.createPolicy(policyParameters);
const policy = await this.createPolicy(web3Provider, policyParameters);
return await policy.generatePreEnactedPolicy(ursulas);
}

Expand All @@ -99,10 +94,11 @@ export class Alice {
}

private async createPolicy(
web3Provider: ethers.providers.Web3Provider,
rawParameters: BlockchainPolicyParameters
): Promise<BlockchainPolicy> {
const { bob, label, threshold, shares, startDate, endDate } =
await this.validatePolicyParameters(rawParameters);
await this.validatePolicyParameters(web3Provider, rawParameters);
const { delegatingKey, verifiedKFrags } = this.generateKFrags(
bob,
label,
Expand All @@ -123,6 +119,7 @@ export class Alice {
}

private async validatePolicyParameters(
web3Provider: ethers.providers.Web3Provider,
rawParams: BlockchainPolicyParameters
): Promise<BlockchainPolicyParameters> {
const startDate = rawParams.startDate ?? new Date();
Expand All @@ -144,8 +141,8 @@ export class Alice {
);
}

const blockNumber = await this.web3Provider.getBlockNumber();
const block = await this.web3Provider.getBlock(blockNumber);
const blockNumber = await web3Provider.getBlockNumber();
const block = await web3Provider.getBlock(blockNumber);
const blockTime = new Date(block.timestamp * 1000);
if (endDate < blockTime) {
throw new Error(
Expand Down
21 changes: 9 additions & 12 deletions src/characters/bob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@ import {
Signer,
} from '@nucypher/nucypher-core';

import { Configuration } from '../config';
import { Keyring } from '../keyring';
import { PolicyMessageKit } from '../kits/message';
import { RetrievalResult } from '../kits/retrieval';
import { PorterClient } from '../porter';
import { zip } from '../utils';

import { Porter } from './porter';

export class RemoteBob {
private constructor(
public readonly decryptingKey: PublicKey,
Expand All @@ -37,11 +35,9 @@ export class RemoteBob {
}

export class Bob {
private readonly porter: Porter;
private readonly keyring: Keyring;

constructor(config: Configuration, secretKey: SecretKey) {
this.porter = new Porter(config.porterUri);
constructor(secretKey: SecretKey) {
this.keyring = new Keyring(secretKey);
}

Expand All @@ -57,24 +53,23 @@ export class Bob {
return this.keyring.signer;
}

public static fromSecretKey(
config: Configuration,
secretKey: SecretKey
): Bob {
return new Bob(config, secretKey);
public static fromSecretKey(secretKey: SecretKey): Bob {
return new Bob(secretKey);
}

public decrypt(messageKit: MessageKit | PolicyMessageKit): Uint8Array {
return this.keyring.decrypt(messageKit);
}

public async retrieveAndDecrypt(
porterUri: string,
policyEncryptingKey: PublicKey,
publisherVerifyingKey: PublicKey,
messageKits: readonly MessageKit[],
encryptedTreasureMap: EncryptedTreasureMap
): Promise<readonly Uint8Array[]> {
const policyMessageKits = await this.retrieve(
porterUri,
policyEncryptingKey,
publisherVerifyingKey,
messageKits,
Expand Down Expand Up @@ -103,6 +98,7 @@ export class Bob {
}

public async retrieve(
porterUri: string,
policyEncryptingKey: PublicKey,
publisherVerifyingKey: PublicKey,
messageKits: readonly MessageKit[],
Expand All @@ -122,7 +118,8 @@ export class Bob {
);

const retrievalKits = policyMessageKits.map((pk) => pk.asRetrievalKit());
const retrieveCFragsResponses = await this.porter.retrieveCFrags(
const porter = new PorterClient(porterUri);
const retrieveCFragsResponses = await porter.retrieveCFrags(
treasureMap,
retrievalKits,
publisherVerifyingKey,
Expand Down
Loading
Loading