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

Commit

Permalink
Draft snap integration (#69)
Browse files Browse the repository at this point in the history
* Add basic snap template.

* Experiment with snap_manageState.

* Setup watch script to build snap.

* Update meta data and move to rpc directory.

* Update script, remove unused script.

* Work in progress migrating to client package.

Currently webpack cannot find the typescript definitions.

* Configure shared package for client code.

* Rename client -> demo.

* Rename server -> cli.

* Update Dockerfile.

* Move shared Javascript and webassembly to packages.

* Format and update CI tasks.

* Experiment with getting the bip44 entropy.

* Set up linting for client package.

* Fix lint errors in client.

Updated JsDoc comments, closes #65.

* Skeleton react frontend for snap.

* Continue frontend setup with parcel and react@18.

Need the typescript definitions for window.ethereum.

See: MetaMask/types#16

* Fix type checking for ethereum global.

* Remove React.StrictMode as it renders twice.

* Trying to figure out snap/parcel workflow.

* Draft logic for running snap in parcel.

* Stub wasm library for snap utilities.

* Add xchacha20poly1305 wasm utilities.

* Add redux store.

* Exploring state management in redux.

* Tidy dapp meta data.

* Improve dapp scripts.

* Routines for encryption and decryption.

* Initial state persistence logic.

Exposes the thunks for clearState(), loadState() and saveState().

The state is an array of KeyShare instances which currently just
encapsulate a label but later will also include the key share
information generated using multi-party-ecdsa.

The data is encrypted and decrypted using xchacha20poly1305 functions
that are exposed via webassembly in the @metamask/mpc-snap-wasm package.

Currently, the webassembly is executed on the main UI thread as we are
dealing with small amounts of data it should execute fast enough.

* Add material and theme handling.

* Sketch connect page.

* Show snackbar on connect failure.

* Setup prettier for dapp.

* Set up linting.

* Navigate to keys view on connect.

* Add route for creating a key share.

* Sketch layout for parameters view.

* Improve key share creation layout.

* Create group and draft invitation layout.

* Improve layout on invite screen.

* Pass parameters to join screen.

* Improve flow for joining keygen sessions.

* Prepare views for joining key generation.

* Prepare session info for key generation.

* Preparing for web worker.

* Wait for web worker to be ready.

So that we are sure we can call the web worker webassembly functions when we need them.

* Prepare helper to cleanup listeners.

* Generate key share, draft save layout.

* Draft logic for listing key shares.

* Improve listing of key shares.

* Improve key listing view.

* Prepare for explicit action to save key share.

* Draft key share view.

* Show balance and chain on key view.

* Support deleting key shares.

* Draft support for deleting key shares.

Requires work to show a dialog to confirm the permanent deletion of the key
share.

* Improve navigation when deleting key shares.

* Set up dialogs reducer.

* Use confirmation dialog for key share deletion.

* Improve snackbar handling.

Use redux dispatch with a singleton component.

* Prepare keystore import/export WASM functions.

* Support exporting key shares.

* Show alert when exporting a key share.

* Prepare import key store dialog.

* Improve logic for loading key shares.

Prepare to gather password for keystore import.

* Fix file input bug.

* Draft logic for importing a key share.

* Use stepper for key share import.

* Add beta chip and warning.

* Prepare sign links and routes.

* Form for message to sign.

* Improve lazy loading of key shares.

So that deep links to key shares and signing work as expected.

* Expose keccak256 helper function in snap wasm.

* Improve invite card layout.

Prepare for separate links for each invite which will help prevent a
rushing adversary attack, see #70.

* Helper functions for creating and joining groups.

So that this logic can be shared between the key generation and signing
flows.

* Prepare signing session join invites.

* Prepare views to join signing session.

* Support public associated data with a session.

This will allow signing sessions to associate some public information
with the session and clients that join a session to pull that
information from the server.

This will deprecates the logic for proposal notifications.

* Improve doc comments.

* Show message preview when joining sign session.

* Prepare signing approval view.

* Move session state to separate reducer.

The keys reducer has no business dealing with the session information.

* Load party number into session.

This is done for the person creating the message to sign but we also
still need to do it for the parties joining the signing session.

* WIP on compute view.

* Prepare sign candidate state.

* Fix off by one error for sessionLoad event.

Needs to load the party number into the session before testing the
threshold for the notification handling.

* Prepare data for signing.

* Find signing key share.

Currently hanging on round zero when signing, requires debugging.

* Debugging signing participants.

* Improve ListenerCleanup handling.

* Fix expected number of messages for signing.

When preparing the transports we needed a conditional to set the
expected number of messages correctly for signing sessions.

* Switch from sha256 to keccak256 for the demo.

So that the demo and snap code are in sync for the message type used for
signing.

* Update SignResult types.

So it matches the value returned from webassembly.

* Prepare the save proof screen.

* Move state management to a separate module.

* Prepare for more complex app state.

So that we can also save message signing proofs in the app state.

* Add worker progress view for key generation.

* Support custom onTransition for signing.

* Use worker progress view for signing.

Fix a bug with signing multiple times, needed to clear the redux state
with signCandidate and signProof.

* Support downloading message signing proofs.

* Draft logic for saving signing proofs.

* Support listing message proofs.

* Improve message proof handling.

Support deleting message proofs.

* Fix return bug deleting message proofs.

* Confirmation dialog when deleting message proofs.

* Fix type error.
  • Loading branch information
tmpfs authored May 16, 2022
1 parent 27c3de2 commit d65f468
Show file tree
Hide file tree
Showing 136 changed files with 16,629 additions and 617 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/server.yml → .github/workflows/cli.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
name: Server
name: Cli

on: [pull_request]

env:
RUST_LOG: "0"
WORKING_DIRECTORY: ./server
WORKING_DIRECTORY: ./cli

jobs:
fmt:
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/client.yml → .github/workflows/demo.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
name: Client
name: Demo

on: [pull_request]

env:
WORKING_DIRECTORY: ./client
WASM_DIRECTORY: ./wasm
WORKING_DIRECTORY: ./demo
WASM_DIRECTORY: ./packages/wasm

jobs:
fmt:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/wasm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on: [pull_request]

env:
RUST_LOG: "0"
WORKING_DIRECTORY: ./wasm
WORKING_DIRECTORY: ./packages/wasm

jobs:
fmt:
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ target
dist
pkg
node_modules
.parcel-cache
*.bak
*.log
12 changes: 6 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ ENV PATH=/usr/local/cargo/bin:$PATH

# SERVER
COPY getrandom getrandom
COPY server server
COPY cli cli
RUN rustup override set nightly-2021-08-12
RUN cargo install --path ./server
RUN cargo install --path ./cli
RUN cargo install --version 0.10.2 wasm-pack
RUN mv ~/.cargo/bin/* /usr/bin
RUN mpc-websocket --version
Expand All @@ -36,12 +36,12 @@ RUN cd wasm && wasm-pack build --target web;
# CLIENT
FROM node:14 AS client
WORKDIR /usr/app
COPY client client
COPY demo demo
COPY --from=builder /usr/app/wasm /usr/app/wasm
RUN cd client && yarn install && yarn build
RUN cd demo && yarn install && yarn build

FROM debian:bullseye AS runner
WORKDIR /usr/app
COPY --from=builder /usr/bin/mpc-websocket /usr/bin/mpc-websocket
COPY --from=client /usr/app/client/dist /usr/app/client/dist
CMD mpc-websocket --bind 0.0.0.0:8080 client/dist
COPY --from=client /usr/app/demo/dist /usr/app/demo/dist
CMD mpc-websocket --bind 0.0.0.0:8080 demo/dist
48 changes: 30 additions & 18 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,43 +1,55 @@
wasm:
@cd wasm && wasm-pack build --target web
@cd packages/wasm && wasm-pack build --target web --scope metamask

snap-wasm:
@cd snap/wasm && wasm-pack build --target web --scope metamask

dist: wasm
@cd client && yarn build
@cd demo && yarn build

dist-dev: wasm
@cd client && yarn build:dev
@cd demo && yarn build:dev

module:
@cd module && yarn build

setup: wasm
@cd client && yarn install && npx playwright install
setup: wasm snap-wasm module
@cd demo && yarn install && npx playwright install
@cd module && yarn install
@cd snap/rpc && yarn install
@cd snap/ui && yarn install

build:
@cd server && cargo build
@cd cli && cargo build

release: dist
@cd server && cargo build --release
@cd cli && cargo build --release

server: dist
@cd server && cargo run
@cd cli && cargo run

client:
@cd client && yarn start
demo:
@cd demo && yarn start

test-server: dist-dev
@cd server && cargo run
@cd cli && cargo run

test:
@cd client && yarn test
@cd demo && yarn test

test-headed:
@cd client && yarn test-headed
@cd demo && yarn test-headed

lint:
@cd client && yarn lint
@cd demo && yarn lint
@cd packages/client && yarn lint
@cd snap/dapp && yarn lint

fmt: lint
@cd client && yarn prettier
@cd demo && yarn fmt
@cd library && cargo fmt
@cd server && cargo fmt
@cd wasm && cargo fmt
@cd cli && cargo fmt
@cd packages/wasm && cargo fmt
@cd snap/wasm && cargo fmt

.PHONY: wasm dist dist-dev setup build release server client test lint fmt
.PHONY: wasm snap-wasm dist dist-dev module setup build release server demo test lint fmt
33 changes: 22 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ cargo install --version 0.10.2 wasm-pack

## Structure

* `client`: Browser web application.
* `cli`: Command line interface for the server.
* `demo`: Browser web application.
* `getrandom`: Hack for webassembly compilation (see [getrandom notes](#getrandom)).
* `library`: Websocket server library.
* `server`: Command line interface for the server.
* `wasm`: Webassembly bindings to [multi-party-ecdsa][].
* `snap`: Experimental snap for MetaMask.
* `packages`: Javascript packages and webassembly bindings to [multi-party-ecdsa][].

## Setup

Expand All @@ -34,18 +35,28 @@ make setup
# Start the server on ws://localhost:3030
make server
# Start the client on http://localhost:8080
make client
make demo
```

Now visit `http://localhost:8080`.

## Development

During development you should link the WASM module:
During development you should link the WASM module and Javascript client package:

```
(cd wasm/pkg && yarn link)
(cd client && yarn link ecdsa-wasm)
(cd packages/wasm/pkg && yarn link)
(cd demo && yarn link @metamask/mpc-ecdsa-wasm)
(cd packages/client && yarn link)
(cd demo && yarn link @metamask/mpc-client)
```

To work on the snap there are some additional webassembly utilities used for encrypting and decrypting key shares:

```
(cd snap/wasm/pkg && yarn link)
(cd snap/dapp && yarn link @metamask/mpc-snap-wasm)
```

## Test
Expand All @@ -62,16 +73,16 @@ If you want to see the tests execute in a browser run `make test-headed`.
To hack on the code whilst running the tests open several terminal sessions:

```
cd server && cargo run
cd client && yarn start
cd client && TEST_URL=http://localhost:8080 yarn test
cd cli && cargo run
cd demo && yarn start
cd demo && TEST_URL=http://localhost:8080 yarn test
```

Networking is racy and we have fixed quite a few race conditions so to it is a good idea to run the tests lots of times:

```
make test-server # start the backend server
(cd client && ./test.sh) # run the tests 100 times
(cd demo && ./test.sh) # run the tests 100 times
```

## Docker
Expand Down
2 changes: 1 addition & 1 deletion server/Cargo.lock → cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

File renamed without changes.
File renamed without changes.
18 changes: 17 additions & 1 deletion server/src/bin/main.rs → cli/src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,21 @@ async fn main() -> Result<()> {
let opts: Options = Parser::parse();
let bind = opts.bind.unwrap_or_else(|| "127.0.0.1:3030".to_string());
let addr = SocketAddr::from_str(&bind)?;
Server::start("mpc", (addr.ip(), addr.port()), opts.files).await

let static_files = if let Some(static_files) = opts.files {
if static_files.is_absolute() {
static_files
} else {
let cwd = std::env::current_dir()?;
cwd.join(static_files)
}
} else {
let mut static_files = std::env::current_dir()?;
static_files.pop();
static_files.push("demo");
static_files.push("dist");
static_files
};

Server::start("mpc", (addr.ip(), addr.port()), static_files).await
}
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes.
9 changes: 6 additions & 3 deletions client/package.json → demo/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"name": "ecdsa-wasm-demo",
"name": "@metamask/mpc-ecdsa-wasm",
"version": "0.2.0",
"prviate": true,
"description": "Experimental demo of multi-party threshold ECDSA in WASM",
"main": "index.js",
"scripts": {
"build": "webpack --config webpack.config.js --node-env production",
"build:dev": "webpack --config webpack.config.js --node-env development",
"start": "webpack-dev-server --no-client-overlay",
"prettier": "prettier --write . --no-color",
"fmt": "prettier --write . --no-color",
"lint": "eslint src",
"test:headed": "FORCE_COLOR=0 playwright test --headed",
"test": "FORCE_COLOR=0 playwright test"
Expand All @@ -29,10 +30,12 @@
},
"homepage": "https://github.com/LavaMoat/ecdsa-wasm",
"dependencies": {
"@metamask/mpc-client": "file:../packages/client",
"@metamask/mpc-ecdsa-wasm": "file:../packages/wasm/pkg",
"@reduxjs/toolkit": "^1.7.1",
"@types/uuid": "^8.3.4",
"comlink": "^4.3.1",
"ecdsa-wasm": "file:../wasm/pkg",
"events": "^3.3.0",
"history": "^5.2.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
Expand Down
16 changes: 7 additions & 9 deletions client/src/index.tsx → demo/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,12 @@ const App = () => {
};

ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<HashRouter>
<WebSocketProvider>
<App />
</WebSocketProvider>
</HashRouter>
</Provider>
</React.StrictMode>,
<Provider store={store}>
<HashRouter>
<WebSocketProvider>
<App />
</WebSocketProvider>
</HashRouter>
</Provider>,
document.querySelector("main")
);
2 changes: 1 addition & 1 deletion client/src/key-storage.ts → demo/src/key-storage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { KeyShare } from "./mpc";
import { KeyShare } from "@metamask/mpc-client";

const KEY = "keys";

Expand Down
3 changes: 2 additions & 1 deletion client/src/routes/home.tsx → demo/src/routes/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import React, { useState, useEffect, useContext } from "react";
import { useSelector, useDispatch } from "react-redux";
import { groupSelector } from "../store/group";
import { useNavigate } from "react-router-dom";
import { Parameters } from "../mpc";
import { Parameters } from "@metamask/mpc-client";

import { WebSocketContext } from "../websocket-provider";
import { setGroup } from "../store/group";

Expand Down
19 changes: 12 additions & 7 deletions client/src/routes/keygen.tsx → demo/src/routes/keygen.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { Component, useEffect, useContext } from "react";
import { useSelector, useDispatch, connect } from "react-redux";
import { groupSelector, GroupInfo, setGroup } from "../store/group";
import { groupSelector, setGroup } from "../store/group";
import { setKeygenSession, setKeyShare } from "../store/keygen";
import { useParams, useNavigate, NavigateFunction } from "react-router-dom";
import { WebSocketContext } from "../websocket-provider";
Expand All @@ -14,10 +14,16 @@ import {
KeyStorage,
} from "../key-storage";

import { KeyShare, Session, SessionKind, EcdsaWorker } from "../mpc";
import { generateKeyShare } from "../mpc/keygen";

import { WebSocketStream, WebSocketSink } from "../mpc/transports/websocket";
import {
GroupInfo,
KeyShare,
Session,
SessionKind,
EcdsaWorker,
WebSocketStream,
WebSocketSink,
generateKeyShare,
} from "@metamask/mpc-client";

const copyToClipboard = async (
e: React.MouseEvent<HTMLElement>,
Expand Down Expand Up @@ -148,7 +154,6 @@ class Keygen extends Component<KeygenProps, KeygenStateProps> {
);

const keyShare = await generateKeyShare(
websocket,
worker,
stream,
this.state.sink,
Expand Down Expand Up @@ -222,7 +227,7 @@ class Keygen extends Component<KeygenProps, KeygenStateProps> {

const session = await websocket.rpc({
method: "Session.create",
params: [this.props.group.uuid, SessionKind.KEYGEN],
params: [this.props.group.uuid, SessionKind.KEYGEN, null],
});

this.props.dispatch(setKeygenSession(session));
Expand Down
Loading

0 comments on commit d65f468

Please sign in to comment.