Skip to content
This repository has been archived by the owner on Oct 19, 2023. It is now read-only.

Commit

Permalink
Merge pull request #14 from nucypher/tdec-poc
Browse files Browse the repository at this point in the history
  • Loading branch information
piotr-roslaniec authored Jun 27, 2023
2 parents a78f401 + 5434704 commit 5e37235
Show file tree
Hide file tree
Showing 11 changed files with 943 additions and 668 deletions.
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']
node: ["16.x"]
os: [ubuntu-latest]

steps:
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"lint": "echo 'Not implemented'"
},
"dependencies": {
"@nucypher/nucypher-ts": "^1.0.0-beta.1",
"@nucypher/nucypher-ts": "^1.0.0-alpha.0",
"@usedapp/core": "^1.1.5",
"buffer": "^6.0.3",
"ethers": "^5.7.1",
Expand All @@ -36,4 +36,4 @@
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.11.1"
}
}
}
122 changes: 67 additions & 55 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,67 +1,91 @@
import type {
MessageKit,
PolicyMessageKit,
DeployedStrategy,
ConditionSet,
conditions,
Ciphertext,
DeployedCbdStrategy,
} from "@nucypher/nucypher-ts";
import React, { useState } from "react";
import { useEthers } from "@usedapp/core";
import { CbdStrategy, Cohort, FerveoVariant } from "@nucypher/nucypher-ts";
import React, { useEffect, useState } from "react";
import { Mumbai, useEthers } from "@usedapp/core";
import { ethers } from "ethers";

import { ConditionBuilder } from "./ConditionBuilder";
import { Encrypt } from "./Encrypt";
import { Decrypt } from "./Decrypt";
import { Spinner } from "./Spinner";
import { StrategyBuilder } from "./StrategyBuilder";

export type EncryptedMessage = { ciphertext: Ciphertext; };
export default function App() {
const { activateBrowserWallet, deactivate, account } = useEthers();
const { activateBrowserWallet, deactivate, account, switchNetwork } =
useEthers();

const [loading, setLoading] = useState(false);
const [deployedStrategy, setDeployedStrategy] = useState<DeployedStrategy>();
const [conditions, setConditions] = useState<ConditionSet>();
const [encryptedMessage, setEncryptedMessage] = useState<MessageKit>();
const [decryptedMessage, setDecryptedMessage] = useState("");
const [deployedStrategy, setDeployedStrategy] =
useState<DeployedCbdStrategy>();
const [conditionExpr, setConditionExpr] =
useState<conditions.ConditionExpression>();
const [encryptedMessage, setEncryptedMessage] = useState<EncryptedMessage>();
const [decryptedMessage, setDecryptedMessage] = useState<string>();
const [decryptionErrors, setDecryptionErrors] = useState<string[]>([]);

const encryptMessage = (plaintext: string) => {
useEffect(() => {
const makeCohort = async () => {
const cohortConfig = {
threshold: 2,
shares: 2,
porterUri: "https://porter-lynx.nucypher.community",
};
const cohort = await Cohort.create(cohortConfig);
console.log("Created cohort: ", cohort);
return cohort;
};

const deployStrategy = async () => {
const web3Provider = new ethers.providers.Web3Provider(window.ethereum);
const cohort = await makeCohort();
const strategy = CbdStrategy.create(cohort);
console.log("Created strategy: ", strategy);
// TODO: Instead of making a new strategy, we should be able to use the pre-made ritual
const deployedStrategy = await strategy.deploy(web3Provider);
setDeployedStrategy(deployedStrategy);
console.log("Deployed Strategy: ", deployedStrategy);
};

setLoading(true);
deployedStrategy!.encrypter.conditions = conditions;
const encryptedMessage =
deployedStrategy!.encrypter.encryptMessage(plaintext);
deployStrategy().then(() => setLoading(false));
}, []);

const encryptMessage = (plaintext: string) => {
if (!deployedStrategy || !conditionExpr) {
return;
}
setLoading(true);
const encryptedMessage = deployedStrategy
.makeEncrypter(conditionExpr)
.encryptMessageCbd(plaintext);
setEncryptedMessage(encryptedMessage);
setLoading(false);
};

const decryptMessage = async (ciphertext: MessageKit) => {
const decryptMessage = async (encryptedMessage: EncryptedMessage) => {
if (!deployedStrategy || !conditionExpr) {
return;
}
setLoading(true);
setDecryptedMessage("");
setDecryptionErrors([]);

await switchNetwork(Mumbai.chainId);

const web3Provider = new ethers.providers.Web3Provider(window.ethereum);
const retrievedMessages = await deployedStrategy!.decrypter.retrieve(
[ciphertext],
web3Provider
);
const decryptedMessages = retrievedMessages.map((mk: PolicyMessageKit) => {
if (mk.isDecryptableByReceiver()) {
return deployedStrategy!.decrypter.decrypt(mk);
}

// If we are unable to decrypt, we may inspect the errors and handle them
if (Object.values(mk.errors).length > 0) {
const ursulasWithErrors: string[] = Object.entries(mk.errors).map(
([address, error]) => `${address} - ${error}`
);
setDecryptionErrors(ursulasWithErrors);
} else {
setDecryptionErrors([]);
}
return new Uint8Array([]);
});

setDecryptedMessage(new TextDecoder().decode(decryptedMessages[0]));
const decryptedMessage =
await deployedStrategy.decrypter.retrieveAndDecrypt(
web3Provider,
conditionExpr,
FerveoVariant.Precomputed,
encryptedMessage.ciphertext
);

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

Expand All @@ -84,36 +108,24 @@ export default function App() {
<h2>Web3 Provider</h2>
<button onClick={deactivate}> Disconnect Wallet</button>
{account && <p>Account: {account}</p>}
<p>
Access{" "}
<a href={"https://goerli-nfts.vercel.app/"} target="_blank">
the NFT Faucet
</a>{" "}
if needed
</p>
</div>

<StrategyBuilder
setLoading={setLoading}
setDeployedStrategy={setDeployedStrategy}
/>

<ConditionBuilder
enabled={!!deployedStrategy}
conditions={conditions}
setConditions={setConditions}
conditionExpr={conditionExpr}
setConditions={setConditionExpr}
/>

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

<Decrypt
enabled={!!encryptedMessage}
decrypt={decryptMessage}
decryptedMessage={decryptedMessage}
decryptedMessage={decryptedMessage ?? ""}
decryptionErrors={decryptionErrors}
/>
</div>
Expand Down
71 changes: 29 additions & 42 deletions src/ConditionBuilder.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,30 @@
import { ConditionSet, Condition, Conditions } from "@nucypher/nucypher-ts";
import { conditions } from "@nucypher/nucypher-ts";
import React, { useState } from "react";
import { Goerli, useEthers } from "@usedapp/core";
import { Mumbai, useEthers } from "@usedapp/core";

interface Props {
conditions?: ConditionSet;
setConditions: (value: ConditionSet) => void;
conditionExpr?: conditions.ConditionExpression;
setConditions: (value: conditions.ConditionExpression) => void;
enabled: boolean;
}

export const ConditionBuilder = ({
conditions,
conditionExpr,
setConditions,
enabled
enabled,
}: Props) => {
const { library } = useEthers();
const NFTBalanceConfig = {
contractAddress: '0x932Ca55B9Ef0b3094E8Fa82435b3b4c50d713043', // https://goerli-nfts.vercel.app/
standardContractType: 'ERC721',
chain: Goerli.chainId,
method: 'ownerOf',
parameters: [118],
const RpcCondition = {
chain: Mumbai.chainId,
method: "eth_getBalance",
parameters: [":userAddress"],
returnValueTest: {
comparator: '==',
value: ':userAddress',
comparator: ">",
value: "0",
},
};
const DEMO_CONDITION = JSON.stringify(NFTBalanceConfig);
const [conditionjson, setConditionJSON] = useState(
DEMO_CONDITION
);

const DEMO_CONDITION = JSON.stringify(RpcCondition);
const [conditionJson, setConditionJson] = useState(DEMO_CONDITION);

if (!enabled || !library) {
return <></>;
Expand All @@ -44,37 +39,29 @@ export const ConditionBuilder = ({
onChange={(e: any) => onChange(e.target.value)}
defaultValue={defaultValue}
>
{ }
{}
</textarea>
);


const ConditionJSONInput = makeInput(
setConditionJSON,
DEMO_CONDITION
);

const ConditionJSONInput = makeInput(setConditionJson, DEMO_CONDITION);

const onCreateCondition = (e: any) => {
e.preventDefault();
setConditions(new ConditionSet([new Conditions.Condition(JSON.parse(conditionjson))]));
setConditions(
new conditions.ConditionExpression(
new conditions.Condition(JSON.parse(conditionJson))
)
);
};

const ConditionList =
conditions && conditions?.conditions.length > 0 ? (
<div>
<h3>Condition JSON Preview</h3>
<pre>
{conditions?.conditions.map((condition, index) => (
<div key={index}>
{JSON.stringify((condition as Condition).toObj(), null, 2)}
</div>
))}
</pre>
</div>
) : (
<></>
);
const ConditionList = conditionExpr && (
<div>
<h3>Condition JSON Preview</h3>
<pre>
<div>{JSON.stringify(conditionExpr.toObj(), null, 2)}</div>
</pre>
</div>
);

return (
<>
Expand Down
15 changes: 9 additions & 6 deletions src/Decrypt.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { MessageKit } from "@nucypher/nucypher-ts";
import { Ciphertext } from "@nucypher/nucypher-ts";
import React, { useState } from "react";
import type { EncryptedMessage } from "./App";

interface Props {
enabled: boolean;
decrypt: (ciphertext: MessageKit) => void;
decrypt: (encryptedMessage: EncryptedMessage) => void;
decryptedMessage: string;
decryptionErrors: string[];
}
Expand All @@ -21,8 +22,10 @@ export const Decrypt = ({
}

const onDecrypt = () => {
const b64decoded = Buffer.from(ciphertext, "base64");
decrypt(MessageKit.fromBytes(b64decoded));
const encryptedMessage = {
ciphertext: Ciphertext.fromBytes(Buffer.from(ciphertext, "base64")),
};
decrypt(encryptedMessage);
};

const DecryptedMessage = () => {
Expand All @@ -45,7 +48,7 @@ export const Decrypt = ({
return (
<div>
<h2>Decryption Errors</h2>
<p>Not enough cFrags retrieved to open capsule.</p>
<p>Not enough decryption shares to decrypt the message.</p>
<p>Some Ursulas have failed with errors:</p>
<ul>
{decryptionErrors.map((error, index) => (
Expand All @@ -61,7 +64,7 @@ export const Decrypt = ({
<h2>Step 3 - Decrypt Encrypted Message</h2>
<input
value={ciphertext}
placeholder="Enter encrypted message"
placeholder="Enter ciphertext"
onChange={(e) => setCiphertext(e.currentTarget.value)}
/>
<button onClick={onDecrypt}>Decrypt</button>
Expand Down
Loading

0 comments on commit 5e37235

Please sign in to comment.