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

Refactor packages APIs #301

Merged
merged 24 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b58152c
chore(examples): add a nextjs example
piotr-roslaniec Sep 21, 2023
b8bb41b
chore(refactor): refactor package tests
piotr-roslaniec Sep 22, 2023
2e6740e
chore(refactor): refactor taco package internals from character based…
piotr-roslaniec Sep 22, 2023
5927d78
chore(refactor): divide shared package into pre and taco
piotr-roslaniec Sep 22, 2023
a5e6d8b
chore(examples): add taco examples
piotr-roslaniec Sep 25, 2023
da71bf0
chore(test): add multichain condition tests
piotr-roslaniec Sep 26, 2023
7de1be7
chore(deps): deprecate support for node 16
piotr-roslaniec Sep 26, 2023
58254d0
chore: fix vitest test config
piotr-roslaniec Sep 27, 2023
737b215
feat(alpha-13): update coordinator contract
piotr-roslaniec Sep 29, 2023
71b3089
feat(alpha-13): check authorization before encrypting
piotr-roslaniec Sep 29, 2023
2aa2720
chore(examples): update taco/nodejs example
piotr-roslaniec Sep 29, 2023
1292f31
chore(demos): add demos/taco-demo
piotr-roslaniec Sep 29, 2023
5051b03
chore(examples): update examples for alpha-13
piotr-roslaniec Sep 29, 2023
a417a00
chore: remove redundant lockfile
piotr-roslaniec Oct 2, 2023
ed40754
chore: document legacy demos
piotr-roslaniec Oct 2, 2023
719f6a0
chore(demos): add demos/taco-nft-demo
piotr-roslaniec Oct 3, 2023
f3fc336
feat!: deprecate pre-tdec
piotr-roslaniec Oct 4, 2023
7752c24
Update examples/README.md
piotr-roslaniec Oct 5, 2023
9042753
Update examples/taco/webpack-5/src/index.html
piotr-roslaniec Oct 5, 2023
d5c70a5
Update demos/taco-demo/README.md
piotr-roslaniec Oct 5, 2023
acb0fa1
fix(test): fix pre tests after a bad rebase
piotr-roslaniec Oct 6, 2023
fbc90e1
feature(demos): add ritual id input to demos
piotr-roslaniec Oct 6, 2023
d2c4bf7
fix(conditions): rpc condition parameters not checking length
piotr-roslaniec Oct 6, 2023
0a13562
feature(allowlist): expose a method to check encrypter authorization
piotr-roslaniec Oct 6, 2023
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
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node: [ '16.x', '18.x' ]
node: [ '18.x' ]
os: [ ubuntu-latest ]

steps:
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ contracts/compiled
examples/*/pnpm-lock.yaml
pnpm-debug.log
docs-json
docs
./docs
.next
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
16
18
7 changes: 7 additions & 0 deletions demos/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# `@nucypher/*` Demos

- [`taco-demo`](./taco-demo) - A demo of the `@nucypher/taco` library.
- [`taco-nft-demo`](./taco-nft-demo) - A demo an NFT-based condition using the `@nucypher/taco` library.
- [`nucypher-ts-demo`](https://github.com/nucypher/nucypher-ts-demo) - A demo of PRE in the `nucypher-ts` library.
- [`tdec-sandbox`](https://github.com/nucypher/tdec-sandbox) - A demo of tDec in the `nucypher-ts` library.
- [`tdec-nft-example`](https://github.com/nucypher/tdec-nft-example) - A demo of tDec in the `nucypher-ts` library.
Comment on lines +5 to +7
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the spirit of the monorepo, shall we also internalize these demos to this repo?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Internalizing nucypher-ts-demo will require releasing @nucypher/pre, which I think we should postpone. tdec-sandbox and tdec-nft-example will be deprecated (archived) and replaced by existing demos.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also generally agree with the division of examples/demos mentioned above 👍🏻

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tdec-sandbox and tdec-nft-example will be deprecated (archived) and replaced by existing demos.

^ If we are going to deprecate/archive these, should we remove these in the readme then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove them from the README.md when we have a working alternative.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These things are easy to forget. If you are going to leave them for now, I'd recommend filing an issue and setting the appropriate project on it, so that it can be followed up on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked in #308

31 changes: 31 additions & 0 deletions demos/taco-demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# taco-demo

## Installation

```bash
pnpm install
pnpm start
```

## Usage

In order to run this demo will need a MetaMask with an account funded with some
$MATIC.

In order to connect with the network, the demo uses a public instances of
[Porter](https://docs.threshold.network/app-development/threshold-access-control-tac/porter).

### Polygon

`@nucypher/taco` is in an early release. We recommend **not** using it in
production _just yet_.
Comment on lines +18 to +21
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this for a demo readme?


### Tapir - Mumbai Testnet

The current release of `@nucypher/taco` supports Ursulas working on Tapir
network and contracts deployed on Mumbai testnet.

## References

This dApp is based on
[`useDapp` example](https://github.com/EthWorks/useDapp/tree/master/packages/example).
38 changes: 38 additions & 0 deletions demos/taco-demo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "taco-demo",
"version": "0.1.0",
"description": "A usage example for @nucypher/taco",
"private": true,
"author": "Piotr Rosłaniec <p.roslaniec@gmail.com>",
"scripts": {
"start": "webpack serve --mode development",
"build": "tsc --noEmit && rimraf build && webpack --mode production --progress"
},
"dependencies": {
"@nucypher/taco": "workspace:*",
"@usedapp/core": "^1.2.13",
"buffer": "^6.0.3",
"ethers": "^5.7.1",
"file-loader": "^6.2.0",
"react": "^18.2.0",
"react-copy-to-clipboard": "^5.1.0",
"react-dom": "^18.2.0",
"react-spinners": "^0.13.6"
},
"devDependencies": {
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
"@types/react": "^18.0.20",
"@types/react-copy-to-clipboard": "^5.0.4",
"@types/react-dom": "^18.0.6",
"copy-webpack-plugin": "^11.0.0",
"esbuild-loader": "^2.20.0",
"html-webpack-plugin": "^5.5.0",
"react-refresh": "^0.14.0",
"rimraf": "^3.0.2",
"stream-browserify": "^3.0.0",
"typescript": "^4.8.3",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.11.1"
}
}
122 changes: 122 additions & 0 deletions demos/taco-demo/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import {
conditions,
decrypt,
encrypt,
getPorterUri,
initialize,
ThresholdMessageKit,
} from '@nucypher/taco';
Comment on lines +1 to +8
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look how shiny! 🤩

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does look very clean 🎸

import { Mumbai, useEthers } from '@usedapp/core';
import { ethers } from 'ethers';
import React, { useEffect, useState } from 'react';

import { ConditionBuilder } from './ConditionBuilder';
import { Decrypt } from './Decrypt';
import { Encrypt } from './Encrypt';
import { Spinner } from './Spinner';

export default function App() {
const { activateBrowserWallet, deactivate, account, switchNetwork } =
useEthers();

const [loading, setLoading] = useState(false);
const [condition, setCondition] = useState<conditions.Condition>();
const [encryptedMessage, setEncryptedMessage] =
useState<ThresholdMessageKit>();
const [decryptedMessage, setDecryptedMessage] = useState<string>();
const [decryptionErrors, setDecryptionErrors] = useState<string[]>([]);
const [ritualId, setRitualId] = useState<number>(2);

useEffect(() => {
initialize();
switchNetwork(Mumbai.chainId);
}, []);

const encryptMessage = async (message: string) => {
if (!condition) {
return;
}
Comment on lines +36 to +38
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not obvious to me what the intended flow control is here, are you using condition as some sort of status indication? Why do you return nothing here instead of raising an error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

condition could be undefined in this scope, but that should never happen. This check is for type safety: tsc will coalesce Condition | undefined type into undefined in this scope. I don't think this short demo needs to implement a red path with error messages, validation, etc.

setLoading(true);

await switchNetwork(Mumbai.chainId);

const provider = new ethers.providers.Web3Provider(window.ethereum);
const encryptedMessage = await encrypt(
provider,
message,
condition,
ritualId,
provider.getSigner(),
);

setEncryptedMessage(encryptedMessage);
setLoading(false);
};

const decryptMessage = async (encryptedMessage: ThresholdMessageKit) => {
if (!condition) {
return;
}
setLoading(true);
setDecryptedMessage('');
setDecryptionErrors([]);

const provider = new ethers.providers.Web3Provider(window.ethereum);
const porterUri = getPorterUri('lynx');
const decryptedMessage = await decrypt(
provider,
encryptedMessage,
provider.getSigner(),
porterUri,
);

setDecryptedMessage(new TextDecoder().decode(decryptedMessage));
setLoading(false);
};

if (!account) {
return (
<div>
<h2>Web3 Provider</h2>
<button onClick={() => activateBrowserWallet()}>Connect Wallet</button>
</div>
);
}

if (loading) {
return <Spinner loading={loading} />;
}

return (
<div>
<div>
<h2>Web3 Provider</h2>
<button onClick={deactivate}> Disconnect Wallet</button>
{account && <p>Account: {account}</p>}
</div>

<h2>Ritual ID</h2>
<p>Replace with your own ritual ID</p>
<input type={'number'} value={ritualId} onChange={(e) => setRitualId(parseInt(e.currentTarget.value))} />

<ConditionBuilder
enabled={true}
condition={condition}
setConditions={setCondition}
/>

<Encrypt
enabled={!!condition}
encrypt={encryptMessage}
encryptedMessage={encryptedMessage!}
/>

<Decrypt
enabled={!!encryptedMessage}
decrypt={decryptMessage}
decryptedMessage={decryptedMessage}
decryptionErrors={decryptionErrors}
/>
</div>
);
}
81 changes: 81 additions & 0 deletions demos/taco-demo/src/ConditionBuilder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import {conditions} from '@nucypher/taco';
import {Mumbai, useEthers} from '@usedapp/core';
import React, {useState} from 'react';

interface Props {
condition?: conditions.Condition | undefined;
setConditions: (value: conditions.Condition) => void;
enabled: boolean;
}

const rpcCondition = new conditions.RpcCondition({
conditionType: 'rpc',
chain: Mumbai.chainId,
method: 'eth_getBalance',
parameters: [':userAddress'],
returnValueTest: {
comparator: '>',
value: '0',
},
});

export const ConditionBuilder = ({
condition,
setConditions,
enabled,
}: Props) => {
const {library} = useEthers();

const demoCondition = JSON.stringify((condition ?? rpcCondition).toObj());
const [conditionString, setConditionString] = useState(demoCondition);

if (!enabled || !library) {
return <></>;
}

const prettyPrint = (obj: object | string) => {
if (typeof obj === 'string') {
obj = JSON.parse(obj);
}
return JSON.stringify(obj, null, 2);
};

const makeInput = (
onChange = (e: any) => console.log(e),
defaultValue: string,
) => (
<textarea
rows={15}
onChange={(e: any) => onChange(e.target.value)}
defaultValue={prettyPrint(defaultValue)}
>
{}
</textarea>
);

const conditionJSONInput = makeInput(
setConditionString,
JSON.stringify(rpcCondition.toObj()),
);

const onCreateCondition = (e: any) => {
e.preventDefault();
setConditions(conditions.Condition.fromObj(JSON.parse(conditionString)));
};

return (
<>
<h2>Step 1 - Create A Conditioned Access Policy</h2>
<div>
<div>
<h3>Customize your Conditions</h3>
<div>
<h3>Condition JSON</h3>
{conditionJSONInput}
</div>
</div>
<button onClick={onCreateCondition}>Create Conditions</button>
</div>
</>
);
};
76 changes: 76 additions & 0 deletions demos/taco-demo/src/Decrypt.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { ThresholdMessageKit } from '@nucypher/taco';
import React, { useState } from 'react';

interface Props {
enabled: boolean;
decrypt: (encryptedMessage: ThresholdMessageKit) => void;
decryptedMessage?: string | undefined;
decryptionErrors: string[];
}

export const Decrypt = ({
decrypt,
decryptedMessage,
decryptionErrors,
enabled,
}: Props) => {
const [encryptedMessage, setEncryptedMessage] = useState('');

if (!enabled) {
return <></>;
}

const onDecrypt = () => {
if (!encryptedMessage) {
return;
}
const mkBytes = Buffer.from(encryptedMessage, 'base64');
const mk = ThresholdMessageKit.fromBytes(mkBytes);
decrypt(mk);
};

const DecryptedMessage = () => {
if (!decryptedMessage) {
return <></>;
}
return (
<>
<h3>Decrypted Message:</h3>
<p>{decryptedMessage}</p>
</>
);
};

const DecryptionErrors = () => {
if (decryptionErrors.length === 0) {
return null;
}

return (
<div>
<h2>Decryption Errors</h2>
<p>Not enough decryption shares to decrypt the message.</p>
<p>Some Ursulas have failed with errors:</p>
<ul>
{decryptionErrors.map((error, index) => (
<li key={index}>{error}</li>
))}
</ul>
</div>
);
};

return (
<div>
<h2>Step 3 - Decrypt Encrypted Message</h2>
<input
value={encryptedMessage}
placeholder="Enter encrypted message"
onChange={(e) => setEncryptedMessage(e.currentTarget.value)}
/>
<button onClick={onDecrypt}>Decrypt</button>
{DecryptedMessage()}
{DecryptionErrors()}
</div>
);
};
Loading