Skip to content

Commit

Permalink
[BE Wallet] Add util functions for mnemonics (MystenLabs#1891)
Browse files Browse the repository at this point in the history
Co-authored-by: Clay-Mysten <[email protected]>
  • Loading branch information
666lcz and Clay-Mysten authored May 12, 2022
1 parent 3996636 commit 7b3d8ef
Show file tree
Hide file tree
Showing 7 changed files with 12,396 additions and 7,484 deletions.
8 changes: 7 additions & 1 deletion wallet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,10 @@ Same as above the output is [dist/](./dist/).

## Install the extension to Chrome

After building the app, the extension needs to be installed to Chrome. Follow the steps [here](https://developer.chrome.com/docs/extensions/mv3/getstarted/#unpacked) and install the app from the [dist/](./dist/) directory.
After building the app, the extension needs to be installed to Chrome. Follow the steps to [load an unpacked extension](https://developer.chrome.com/docs/extensions/mv3/getstarted/#unpacked) and install the app from the [dist/](./dist/) directory.

## Testing

```
npm run test
```
8 changes: 8 additions & 0 deletions wallet/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};
19,754 changes: 12,272 additions & 7,482 deletions wallet/package-lock.json

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion wallet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"stylelint:fix": "npm run stylelint:check -- --fix",
"lint": "npm run eslint:check && npm run prettier:check && npm run stylelint:check",
"lint:fix": "npm run eslint:fix && npm run prettier:fix && npm run stylelint:fix",
"start": "concurrently --restart-tries 10 --raw \"npm:build:dev -- --watch\" \"npm:prettier:fix:watch\""
"start": "concurrently --restart-tries 10 --raw \"npm:build:dev -- --watch\" \"npm:prettier:fix:watch\"",
"test": "npx jest"
},
"repository": {
"type": "git",
Expand All @@ -31,6 +32,7 @@
"author": "Mysten Labs <[email protected]>",
"license": "Apache-2.0",
"devDependencies": {
"@types/jest": "^27.5.0",
"@types/node": "^17.0.31",
"@types/react": "^18.0.8",
"@types/react-dom": "^18.0.3",
Expand All @@ -45,6 +47,7 @@
"eslint-config-react-app": "^7.0.1",
"eslint-webpack-plugin": "^3.1.1",
"html-webpack-plugin": "^5.5.0",
"jest": "^28.1.0",
"mini-css-extract-plugin": "^2.6.0",
"onchange": "^7.1.0",
"postcss-loader": "^6.2.1",
Expand All @@ -56,6 +59,7 @@
"stylelint-config-prettier-scss": "^0.0.1",
"stylelint-config-standard-scss": "^3.0.0",
"stylelint-webpack-plugin": "^3.2.0",
"ts-jest": "^28.0.2",
"ts-loader": "^9.3.0",
"ts-node": "^10.7.0",
"tsconfig-paths": "^4.0.0",
Expand All @@ -64,8 +68,11 @@
"webpack-merge": "^5.8.0"
},
"dependencies": {
"@mysten/sui.js": "file:../sdk/typescript",
"bip39-light": "^1.0.7",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"tweetnacl": "^1.0.3",
"webextension-polyfill": "^0.9.0"
}
}
15 changes: 15 additions & 0 deletions wallet/src/utils/cryptography/bip39-light.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

declare module 'bip39-light' {
export function entropyToMnemonic(
entropy: Buffer | string,
wordlist?: string[]
): string;
export function generateMnemonic(
strength?: number,
rng?: (size: number) => Buffer,
wordlist?: string[]
): string;
export function mnemonicToSeed(mnemonic: string, password?: string): Buffer;
}
45 changes: 45 additions & 0 deletions wallet/src/utils/cryptography/mnemonics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import bip39 from 'bip39-light';
import nacl from 'tweetnacl';

import type { Ed25519KeypairData } from '@mysten/sui.js';

/**
* Generate a 12-word random mnemonic and keypair using crypto.randomBytes
* under the hood, defaults to 128-bits of entropy.
* @returns a tuple of mnemonic and keypair. The mnemonics is a 12-word string
* split by spaces.
*/
export function generateMnemonicsAndKeypair(): [string, Ed25519KeypairData] {
const mnemonics = bip39.generateMnemonic();
return [mnemonics, getKeypairFromMnemonics(mnemonics)];
}

/**
* Derive public key and private key from the Mnemonics
* @param mnemonics a 12-word seed phrase
* @returns public key and private key
*/
export function getKeypairFromMnemonics(mnemonics: string): Ed25519KeypairData {
const seed = bip39.mnemonicToSeed(normalizeMnemonics(mnemonics));
return nacl.sign.keyPair.fromSeed(
// keyPair.fromSeed only takes a 32-byte array where `seed` is a 64-byte array
new Uint8Array(seed.toJSON().data.slice(0, 32))
);
}

/**
* Sanitize the mnemonics string provided by user
* @param mnemonics a 12-word string split by spaces that may contain mixed cases
* and extra spaces
* @returns a sanitized mnemonics string
*/
export function normalizeMnemonics(mnemonics: string): string {
return mnemonics
.trim()
.split(/\s+/)
.map((part) => part.toLowerCase())
.join(' ');
}
41 changes: 41 additions & 0 deletions wallet/test/utils/cryptography/mnemonics.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { Base64DataBuffer, Ed25519Keypair } from '@mysten/sui.js';

import {
generateMnemonicsAndKeypair,
getKeypairFromMnemonics,
normalizeMnemonics,
} from '../../../src/utils/cryptography/mnemonics';

describe('mnemonics', () => {
it('generate mnemonics', () => {
const [mnemonics, keypair] = generateMnemonicsAndKeypair();
expect(mnemonics.split(' ').length).toBe(12);
const parsedKeypair = getKeypairFromMnemonics(mnemonics);
expect(parsedKeypair.publicKey).toEqual(keypair.publicKey);
expect(parsedKeypair.secretKey).toEqual(keypair.secretKey);
});

it('normalize', () => {
expect(normalizeMnemonics(' Almost a Seed Phrase')).toEqual(
'almost a seed phrase'
);
});

it('parse mnemonics', () => {
const keypairData = getKeypairFromMnemonics(
'Shoot island position soft burden budget tooth cruel issue economy destroy Above'
);

const keypair = new Ed25519Keypair(keypairData);

expect(new Base64DataBuffer(keypairData.secretKey).toString()).toEqual(
'V3zZEK7eJYJminQdR2tF55mOkFpChvcBuHslkjUB+dTGHsYX9tdbrbyu2sALKKQcfTOiz6DAeMOxQ+RNp159nA=='
);
expect(keypair.getPublicKey().toBase64()).toEqual(
'xh7GF/bXW628rtrACyikHH0zos+gwHjDsUPkTadefZw='
);
});
});

0 comments on commit 7b3d8ef

Please sign in to comment.