Skip to content

Commit

Permalink
feat: tss-lib for react-native
Browse files Browse the repository at this point in the history
  • Loading branch information
ieow committed Dec 11, 2023
1 parent 06480a7 commit f9bb1aa
Show file tree
Hide file tree
Showing 8 changed files with 23,367 additions and 2 deletions.
22,754 changes: 22,754 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

14 changes: 12 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,28 @@
"pod-install": "^0.1.0",
"prettier": "^2.0.5",
"react": "18.2.0",
"react-native": "0.73.0",
"react-native": "0.72.0",
"react-native-builder-bob": "^0.20.0",
"release-it": "^15.0.0",
"turbo": "^1.10.7",
"@toruslabs/eccrypto": ">=4.0.0",
"@toruslabs/tss-client": ">=2.0.0",
"@toruslabs/tss-lib": ">=2.0.0",
"react-native-react-bridge": ">=0.11.2",
"react-native-webview": ">=13.6.0",
"typescript": "^5.0.2"
},
"resolutions": {
"@types/react": "17.0.21"
},
"peerDependencies": {
"react": "*",
"react-native": "*"
"react-native": "*",
"@toruslabs/eccrypto": ">=4.0.0",
"@toruslabs/tss-client": ">=2.0.0",
"@toruslabs/tss-lib": ">=2.0.0",
"react-native-react-bridge": ">=0.11.2",
"react-native-webview": ">=13.6.0"
},
"workspaces": [
"example"
Expand Down
120 changes: 120 additions & 0 deletions src/TssLibBridge/Bridge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import React from 'react';
import WebView from 'react-native-webview';
import { useWebViewMessage } from 'react-native-react-bridge';
import type { Message } from 'react-native-react-bridge';
import webApp from './WebApp';
import { View, StyleSheet } from 'react-native';
import {
TssLibAction,
type TssLibMessageResponse,
type TssLibMessageRequest,
TssLibMessageType,
} from './common';
import '@toruslabs/tss-client';

// let promiseOn;
export let bridgeEmit: (message: Message<any>) => void;

export const resolveMap = new Map<string, any>();
export const rejectMap = new Map<string, any>();

// resolve promise on response
const handleTssLibResponse = (data: TssLibMessageResponse) => {
const { ruid, action, result } = data;
// console.log('tssLib Result', ruid, action, result);
const key = ruid + action;
if (resolveMap.has(key)) {
resolveMap.get(key)(result);
resolveMap.delete(key);
} else {
console.log('tssLib', 'no resolver', key);
}
};

// handle request from webview (js_send_msg, js_read_msg)
const handleTssLibRequest = async (data: TssLibMessageRequest) => {
const { ruid, action, payload } = data;

// console.log('tssLib Request from webview', ruid, action, payload);
if (action === TssLibAction.JsReadMsg) {
const { session, self_index, party, msg_type } = payload;
const result = await (global as any).js_read_msg(
session,
self_index,
party,
msg_type
);
bridgeEmit({
type: TssLibMessageType.TssLibResponse,
data: {
ruid,
action,
result,
},
});
// need to add promise to resolveMap?
// console.log('done', action, result);
} else if (action === TssLibAction.JsSendMsg) {
const { session, self_index, party, msg_type, msg_data } = payload;
const result = await (global as any).js_send_msg(
session,
self_index,
party,
msg_type,
msg_data
);
bridgeEmit({
type: TssLibMessageType.TssLibResponse,
data: {
ruid,
action,
result,
},
});
// need to add promise to resolveMap?
// console.log('done', action, result);
}
};

export const Bridge = () => {
// useWebViewMessage hook create props for WebView and handle communication
// The argument is callback to receive message from React
const { ref, onMessage, emit } = useWebViewMessage(async (message) => {
if (message.type === 'debug') {
console.log('debug', message.data);
}

// response handler
if (message.type === TssLibMessageType.TssLibResponse) {
handleTssLibResponse(message.data as TssLibMessageResponse);
}
if (message.type === TssLibMessageType.TssLibRequest) {
await handleTssLibRequest(message.data as TssLibMessageRequest);
}
if (message.type === 'error') {
console.log('error', message.data);
// rejectMap.get(message.data.ruid + message.data.action)(
}
});
bridgeEmit = emit;

return (
<View style={styles.container}>
<WebView
// ref, source and onMessage must be passed to react-native-webview
ref={ref}
// Pass the source code of React app
source={{ html: webApp }}
onMessage={onMessage}
onError={console.log}
/>
</View>
);
};

const styles = StyleSheet.create({
container: {
height: 0,
display: 'none',
},
});
234 changes: 234 additions & 0 deletions src/TssLibBridge/WebApp/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
import React, { useEffect } from 'react';
import {
webViewRender,
emit,
useNativeMessage,
} from 'react-native-react-bridge/lib/web';
import * as TssLib from '@toruslabs/tss-lib';
import {
TssLibAction,
type TssLibMessageResponse,
type TssLibMessageRequest,
TssLibMessageType,
} from '../common';

const style = {
width: '100vw',
height: '0vh',
margin: 'auto',
backgroundColor: 'lightblue',
};

let bridgeEmit: any;
const debug = (data: any) => {
bridgeEmit({
type: 'debug',
data,
});
};
const error = (data: any) => {
bridgeEmit({
type: 'error',
data,
});
};

let resolverMap = new Map();

if ((globalThis as any).js_read_msg === undefined) {
(globalThis as any).js_read_msg = async function (
session: string,
self_index: number,
party: number,
msg_type: string
) {
const ruid = session + party + msg_type;
// debug({type: 'js_read_msg', msg: 'start', ruid});
bridgeEmit({
type: TssLibMessageType.TssLibRequest,
data: {
ruid,
action: TssLibAction.JsReadMsg,
payload: { session, self_index, party, msg_type },
},
});
const result = await new Promise((resolve) => {
resolverMap.set(ruid + '-js_read_msg', resolve);
// setTimeout(() => {
// reject('timeout');
// }, 5000);
});
console.log('js_read_msg DONE', result);

return result;
};
}

if ((globalThis as any).js_send_msg === undefined) {
(globalThis as any).js_send_msg = async function (
session: string,
self_index: number,
party: number,
msg_type: string,
msg_data?: string
) {
const ruid = session + party + msg_type;
// debug({type: 'js_send_msg', msg: 'start', ruid});
bridgeEmit({
type: TssLibMessageType.TssLibRequest,
data: {
ruid,
action: TssLibAction.JsSendMsg,
payload: { session, self_index, party, msg_type, msg_data },
},
});
const result = await new Promise((resolve) => {
resolverMap.set(ruid + '-js_send_msg', resolve);
});
return result;
};
}

TssLib.default('https://node-1.node.web3auth.io/tss/v1/clientWasm');

async function handleTssLib(
data: TssLibMessageRequest
): Promise<TssLibMessageResponse> {
const { action, payload, ruid } = data as TssLibMessageRequest;
if (action === TssLibAction.BatchSize) {
return { ruid, action, result: TssLib.batch_size() };
}
if (action === TssLibAction.RandomGenerator) {
const { state } = payload;
const result = TssLib.random_generator(state);
return { ruid, action, result };
}
if (action === TssLibAction.RandomGeneratorFree) {
const { rng } = payload;
TssLib.random_generator_free(rng);
return { ruid, action, result: 'done' };
}
if (action === TssLibAction.ThresholdSigner) {
const { session, player_index, player_count, threshold, share, pubkey } =
payload;
try {
const result = TssLib.threshold_signer(
session,
player_index,
player_count,
threshold,
share,
pubkey
);
return { ruid, action, result };
} catch (e) {
return { ruid, action, error: e };
}
}
if (action === TssLibAction.ThresholdSignerFree) {
const { signer } = payload;
TssLib.threshold_signer_free(signer);
return { ruid, action, result: 'done' };
}
if (action === TssLibAction.Setup) {
const { signer, rng } = payload;
//result from setup not able to stringify
await TssLib.setup(signer, rng);
return { ruid, action, result: 'done' };
}
if (action === TssLibAction.Precompute) {
const { parties, signer, rng } = payload;
// conversion to Uint8Array needed as bridge transfer messed up Uint8Array
const result = await TssLib.precompute(
Uint8Array.from(parties),
signer,
rng
);
return { ruid, action, result };
}
if (action === TssLibAction.LocalSign) {
const { msg, hash_only, precompute } = payload;
const result = TssLib.local_sign(msg, hash_only, precompute);
return { ruid, action, result };
}
if (action === TssLibAction.GetRFromPrecompute) {
const { precompute } = payload;
const result = TssLib.get_r_from_precompute(precompute);
return { ruid, action, result };
}
if (action === TssLibAction.LocalVerify) {
const { msg, hash_only, r, sig_frags, pubkey } = payload;
const result = TssLib.local_verify(msg, hash_only, r, sig_frags, pubkey);
return { ruid, action, result };
}
if (action === TssLibAction.Sign) {
const { counterparties, msg, hash_only, signer, rng } = payload;
const result = await TssLib.sign(
counterparties,
msg,
hash_only,
signer,
rng
);
return { ruid, action, result };
}

return { ruid, action, result: 'unknown action' };
}

async function handleTssLibResponse(
data: TssLibMessageResponse
): Promise<TssLibMessageResponse> {
const { action, result, ruid } = data;
if (action === TssLibAction.JsSendMsg) {
resolverMap.get(ruid + '-js_send_msg')(result);
resolverMap.delete(ruid + '-js_send_msg');
console.log('js_send_msg resolved', result);
return { ruid, action, result: 'done' };
}
if (action === TssLibAction.JsReadMsg) {
resolverMap.get(ruid + '-js_read_msg')(result);
resolverMap.delete(ruid + '-js_read_msg');
return { ruid, action, result: 'done' };
}
throw { ruid, action, result: 'done' };
}

const Root = () => {
useNativeMessage(async (message: { type: string; data: any }) => {
if (message.type === TssLibMessageType.TssLibRequest) {
try {
let result = await handleTssLib(message.data);
emit({ type: TssLibMessageType.TssLibResponse, data: result });
} catch (e) {
error({
msg: `${message.type} error`,
payload: message.data,
error: e,
});
}
}
if (message.type === TssLibMessageType.TssLibResponse) {
try {
await handleTssLibResponse(message.data);
} catch (e) {
debug({ type: 'handleTssLibResponse error', e });
error({
msg: `${message.type} error`,
payload: message.data,
error: e,
});
}
}
});

bridgeEmit = emit;

useEffect(() => {
emit({ type: 'tsslibInit', data: 'initializing' });
}, []);

return <div style={style} />;
};

export default webViewRender(<Root />);
Loading

0 comments on commit f9bb1aa

Please sign in to comment.