Quick start

Silent Shard Duo React-Native SDK

The quick start guide will help you to quickly start integrating Silent Shard Duo React-Native SDK into your application. You can use either hooks or async functionsat your will.

Let's start!

Note: All MPC related functions functions(key-generation, sign-generation, key-rotation) are WS requests to the cloud node, and to secure the communications, the application needs a userToken and partyKey(ECDSAP1PartyKeys) to authenticate the user.

As a recap, a client user is authenticated to the server node through the admin which issues a JWT for that user. For the issuance of the token the admin needs a public Ed25519 key for that user which is provided by the user as partyKey. Then the user side has to make an HTTP request to the Admin SDK in order to get the token to be further used in MPC operations for authorization purposes. We have an example of user-side code that queries the Admin SDK to create the token. The flow is as follows: The user creates an Ed25519 signing key pair, queries the Admin SDK for token issuance with a createUserToken call, and the Admin SDK creates the token and returns it to the user.

1. Run server node locally

Please refer to this from the Server Node page.

2. Create an application backend

In this guide using bun run time to create a simple HTTP server to serve userToken JWT using Adming-SDK. Bun 1.0.15 higher is required.

Initialize Project

First, run the following command to initialize the HTTP project.

bun init

# Project name    - simple-backend
# Entry point     - index.ts

Install dependencies

bun add elysia
bun add @elysiajs/cors
bun add git+https://github.com/silence-laboratories/sigpair-admin-v2.git

Create an HTTP server

Populate an index.ts file in the root directory in the root directory with the following content.

import {Elysia, t} from 'elysia'
import {cors} from "@elysiajs/cors";
import {SigpairAdmin} from "sigpair-admin-v2";

// Example admin token.
// DO NOT hardcode in production code!
const adminToken =
  "1ec3804afc23258f767b9d38825dc7ab0a2ea44ef4adf3254e4d7c6059c3b55a";

// Base URL of the server node
const baseUrl = "http://localhost:8080";

const app = new Elysia({
  serve: {
    hostname: '0.0.0.0'
  }
});

app.use(cors());

app.post('/get-access-token', async ({body}) => {
  const admin = new SigpairAdmin(adminToken, cloudNodeUrl);
  const userId = await admin.createUser('test-user-demo-application');
  const accessToken = admin.genUserToken(userId, body.pk);
  return { accessToken };
}, {
  body: t.Object({
    pk: t.String(),
  })
});

app.listen(8000);

console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);

Run HTTP server

bun run index.ts

If successful in the terminal logs 🦊 Elysia is running at 0.0.0.0:8000

3. Mobile application - quick start

Create a fetch function to get userToken JWT from the backend. We will be using this function to generate userToken before each MPC action.

This part assume, Server node and simple backend are running on local PC

Simple backend running at localhost:8000

Server node running at localhost:8080

const SIMPLE_API_URL = "http://localhost:8000";
// For the Android use http://10.0.2.2:8080

export const createUserToken = async (
  userPartyKey: ECDSAP1PartyKeys,
): Promise<string> => {
  const response = await fetch(`${SIMPLE_API_URL}/get-access-token`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      pk: Buffer.from(await userPartyKey.extractPk(), 'base64').toString('hex'),
    }),
  });
  const json = await response.json();
  if (json.accessToken) {
    return json.accessToken;
  }
  throw new Error('Failed to get access token');
};

Key generation quick start

Using the Hooks

import { DuoProvider, useECDSAKeyGen } from '@silencelaboratories/react-native-duo-sdk';
import { ECDSAP1PartyKeys } from '@silencelaboratories/react-native-two-party-ecdsa';
import { Button, Text, View } from 'react-native';

// Base URL of the server node
const cloudNodeUrl = "localhost:8080";
// For the Android use 10.0.2.2:8080

function App() {
  return (
    <DuoProvider
      cloudNodeUrl={cloudNodeUrl}
    >
      <KeygenTest />
    </DuoProvider>
  );
}

function KeygenTest() {
  const { isLoading, keyshare, error, asyncKeygen } = useECDSAKeyGen();

  const handleKeygen = async () => {
    const partyKey = await ECDSAP1PartyKeys.create();
    const userToken = createUserToken(partyKey);
    const localKeyshare = await asyncKeygen({
      userToken,
      partyKey
    });
    console.log('localKeyshare', localKeyshare);
  };

  if (isLoading) {
    return (
      <View>
        <Text>Loading...</Text>
      </View>
    );
  }

  if (error) {
    return (
      <View>
        <Text>Error: {error.message}</Text>
      </View>
    );
  }

  if (keyshare) {
    return (
      <View>
        <Text>Keyshare: {keyshare}</Text>
        <Button onPress={handleKeygen} title="Generate new key keyshare"/>
      </View>
    );
  }

  return (
    <Button onPress={handleKeygen} title="Generate key keyshare"/>
  );
}

Using async functions

import { DuoProvider, useECDSAKeyGen } from '@silencelaboratories/react-native-duo-sdk';
import { ECDSAP1PartyKeys } from '@silencelaboratories/react-native-two-party-ecdsa';
import { Button } from 'react-native';

// Base URL of the server node
const cloudNodeUrl = "localhost:8080";
// For the Android use 10.0.2.2:8080

function App() {
  const handleKeygen = async () => {
    const partyKey = await ECDSAP1PartyKeys.create();
    const userToken = createUserToken(partyKey);
    const keyshare = await keyGenECDSA({
      url: cloudNodeUrl,
      partyKey,
      userToken,
    });
    console.log('keyshare', keyshare);
  };
  
  return (
    <Button onPress={handleKeygen} title="Generate key keyshare"/>
  );
}

ECDSA Signing quick start

Using the Hooks

import { DuoProvider, useGenECDSASign } from '@silencelaboratories/react-native-duo-sdk';
import { ECDSAP1PartyKeys } from '@silencelaboratories/react-native-two-party-ecdsa';
import { Button, Text, View } from 'react-native';

// Base URL of the server node
const cloudNodeUrl = "localhost:8080";
// For the Android use 10.0.2.2:8080

function App() {
  return (
    <DuoProvider
      cloudNodeUrl={cloudNodeUrl}
    >
      <SignTest />
    </DuoProvider>
  );
}

function SignTest() {
  const { isLoading, signature, error, asyncSign } = useGenECDSASign();

  const handleSign = async () => {
    const partyKey = await ECDSAP1PartyKeys.create();
    const userToken = createUserToken(partyKey);
    const keyshare = 'KEYSHARE_GENERATED_FROM_KEYGEN';
    const localSignature = await asyncSign({
      userToken,
      partyKey,
      keyshare,
      hexMessageHash: 'MESSAGE_TO_SIGN_HEX_STRING'
    });
    console.log('localSignature', localSignature);
  };

  if (isLoading) {
    return (
      <View>
        <Text>Loading...</Text>
      </View>
    );
  }

  if (error) {
    return (
      <View>
        <Text>Error: {error.message}</Text>
      </View>
    );
  }

  if (signature) {
    return (
      <View>
        <Text>Signature: {signature}</Text>
        <Button onPress={handleSign} title="Generate new signature"/>
      </View>
    );
  }

  return (
    <Button onPress={handleSign} title="Generate signature"/>
  );
}

Using async functions

import { DuoProvider, signGenECDSA } from '@silencelaboratories/react-native-duo-sdk';
import { ECDSAP1PartyKeys } from '@silencelaboratories/react-native-two-party-ecdsa';
import { Button } from 'react-native';

// Base URL of the server node
const cloudNodeUrl = "localhost:8080";
// For the Android use 10.0.2.2:8080

function App() {
  const handleSigngen = async () => {
    const partyKey = await ECDSAP1PartyKeys.create();
    const userToken = createUserToken(partyKey);
    const keyshare = 'KEYSHARE_GENERATED_FROM_KEYGEN';
    const signature = await signGenECDSA({
      url: cloudNodeUrl,
      userToken,
      partyKey,
      keyshare,
      hexMessageHash: 'MESSAGE_TO_SIGN_HEX_STRING'
    });
    console.log('signature', signature);
  };

  return (
    <Button onPress={handleSigngen} title="Generate key keyshare"/>
);
}

Key Rotation quick start

Using the Hooks

import { DuoProvider, useECDSAKeyRefresh } from '@silencelaboratories/react-native-duo-sdk';
import { ECDSAP1PartyKeys } from '@silencelaboratories/react-native-two-party-ecdsa';
import { Button, Text, View } from 'react-native';

// Base URL of the server node
const cloudNodeUrl = "localhost:8080";
// For the Android use 10.0.2.2:8080

function App() {
  return (
    <DuoProvider
      cloudNodeUrl={cloudNodeUrl}
    >
      <KeyRefreshTest />
    </DuoProvider>
  );
}

function KeyRefreshTest() {
  const { isLoading, keyshare, error, asyncKeyRefresh } = useECDSAKeyRefresh();

  const handleKeyRefresh = async () => {
    const userToken = createUserToken(partyKey);
    const partyKey = await ECDSAP1PartyKeys.create();
    const keyshare = 'KEYSHARE_GENERATED_FROM_KEYGEN';
    const refreshKeyshare = await asyncKeyRefresh({
      userToken,
      partyKey,
      keyshare
    });
    console.log('refreshKeyshare', refreshKeyshare);
  };

  if (isLoading) {
    return (
      <View>
        <Text>Loading...</Text>
      </View>
    );
  }

  if (error) {
    return (
      <View>
        <Text>Error: {error.message}</Text>
      </View>
    );
  }

  if (keyshare) {
    return (
      <View>
        <Text>Keyshare: {keyshare}</Text>
        <Button onPress={handleKeyRefresh} title="Refresh keyshare again"/>
      </View>
    );
  }

  return (
    <Button onPress={handleKeyRefresh} title="Refresh key keyshare"/>
  );
}

Using async functions

import { DuoProvider, keyRefreshECDSA } from '@silencelaboratories/react-native-duo-sdk';
import { ECDSAP1PartyKeys } from '@silencelaboratories/react-native-two-party-ecdsa';

// Base URL of the server node
const cloudNodeUrl = "localhost:8080";
// For the Android use 10.0.2.2:8080

function App() {
  const handleKeyRefresh = async () => {
    const partyKey = await ECDSAP1PartyKeys.create();
    const userToken = createUserToken(partyKey);
    const keyshare = 'OLD_KEYSHARE_GENERATED_FROM_KEYGEN';
    const refreshKeyshare = await keyRefreshECDSA({
      url: cloudNodeUrl,
      partyKey,
      userToken,
      keyshare
    });
    console.log('refreshKeyshare', refreshKeyshare);
  }

  return (
    <Button onPress={handleKeyRefresh} title="Refresh key keyshare" />
  );
}

Last updated