diff --git a/.nycrc.json b/.nycrc.json index 8eaff3db..39cff3c6 100644 --- a/.nycrc.json +++ b/.nycrc.json @@ -8,7 +8,8 @@ "statements": 0, "include": [ "src/fabric/service/**.ts", - "src/quorum/service/**.ts" + "src/quorum/service/**.ts", + "src/wallet/service/**.ts" ], "exclude":[ "src/fabric/service/Service.abstract.ts", diff --git a/docs/wallet/COMMANDS.md b/docs/wallet/COMMANDS.md new file mode 100644 index 00000000..8109cf7a --- /dev/null +++ b/docs/wallet/COMMANDS.md @@ -0,0 +1,19 @@ +# Wallet 指令文件 + +(English version)(Work In Progress) + +## 目錄 + +- [Wallet](#wallet) + +## Wallet + +### `bdk wallet create` + +Description: 產生 Wallet 的相關資訊 + +| Options | Type | Description | Required | Default | +| --------------------- | :-----: | ------------------------------ | :------: | ------- | +| --help | boolean | Show help | | | +| --version | boolean | Show version number | | | +| -i, --interactive | boolean | 是否使用 Cathay BDK 互動式問答 | | | diff --git a/docs/wallet/EXAMPLE.md b/docs/wallet/EXAMPLE.md new file mode 100644 index 00000000..a07f59eb --- /dev/null +++ b/docs/wallet/EXAMPLE.md @@ -0,0 +1,14 @@ +# 使用範例 +(English version)(Work In Progress) + +## 目錄 +- [建立 Wallet](#建立-wallet) + +## 建立 Wallet + +```bash +# 輸入指令,啟動 Wallet 互動式介面 +bdk wallet create -i +``` + +選擇欲建立的 Wallet 類型(預設為 ethereum),確認輸入後即可獲得 `wallet address`、`private key`,請自行妥善保管,若不使用互動模式,則預設為建立 `Ethereum Wallet`。 diff --git a/src/bdk.ts b/src/bdk.ts index 07ef96ca..fae6bf61 100755 --- a/src/bdk.ts +++ b/src/bdk.ts @@ -26,6 +26,7 @@ const argv = yargs }) .commandDir('fabric') .commandDir('quorum') + .commandDir('wallet') .commandDir('hello') .commandDir('ui') .strict() diff --git a/src/quorum/command/network/create.ts b/src/quorum/command/network/create.ts index 6603f57b..eaea7748 100644 --- a/src/quorum/command/network/create.ts +++ b/src/quorum/command/network/create.ts @@ -7,6 +7,8 @@ import { NetworkCreateType } from '../../model/type/network.type' import config from '../../config' import { defaultNetworkConfig } from '../../model/defaultNetworkConfig' import ora from 'ora' +import Wallet from '../../../wallet/service/wallet' +import { WalletType } from '../../../wallet/model/type/wallet.type' export const command = 'create' @@ -24,6 +26,7 @@ export const builder = (yargs: Argv) => { export const handler = async (argv: Arguments) => { const network = new Network(config) + const wallet = new Wallet() // check bdkPath files exist or not (include useless file e.g. .DS_Store) const confirm: boolean = await (async () => { network.createBdkFolder() @@ -102,10 +105,10 @@ export const handler = async (argv: Arguments) => { walletAddress = address } else { - const { address, privateKey } = await network.createWalletAddress() + const { address, privateKey } = wallet.createWalletAddress(WalletType.ETHEREUM) walletAddress = address ora().stopAndPersist({ - text: `Your wallet address: 0x${walletAddress}`, + text: `Your ${WalletType.ETHEREUM} wallet address: 0x${walletAddress}`, symbol: '🔑', }) ora().stopAndPersist({ @@ -121,7 +124,7 @@ export const handler = async (argv: Arguments) => { return { chainId, validatorNumber, memberNumber, alloc } } else { - const { address, privateKey } = await network.createWalletAddress() + const { address, privateKey } = wallet.createWalletAddress(WalletType.ETHEREUM) return defaultNetworkConfig(address, privateKey) } })() diff --git a/src/quorum/service/network.ts b/src/quorum/service/network.ts index 25c45288..04473cf4 100644 --- a/src/quorum/service/network.ts +++ b/src/quorum/service/network.ts @@ -500,17 +500,6 @@ export default class Network extends AbstractService { return { privateKey, publicKey, address } } - /** @ignore */ - public createWalletAddress () { - // TODO: use Shawn's code generate key - const nodekey = ethers.Wallet.createRandom() - const privateKey = nodekey.privateKey.replace(/^0x/, '') - const publicKey = nodekey.publicKey.replace(/^0x04/, '') - const address = nodekey.address.replace(/^0x/, '').toLowerCase() - - return { privateKey, publicKey, address } - } - /** @ignore */ public createBdkFolder () { return this.bdkFile.createBdkFolder() diff --git a/src/wallet/command/create.ts b/src/wallet/command/create.ts new file mode 100644 index 00000000..e6ed657c --- /dev/null +++ b/src/wallet/command/create.ts @@ -0,0 +1,51 @@ +import prompts from 'prompts' +import { Argv, Arguments } from 'yargs' +import Wallet from '../service/wallet' +import { onCancel } from '../../util/error' +import { WalletCreateType, WalletType } from '../model/type/wallet.type' +import ora from 'ora' + +export const command = 'create' + +export const desc = '產生 Wallet 的相關資訊' + +interface OptType { + interactive: boolean +} + +export const builder = (yargs: Argv) => { + return yargs + .example('bdk wallet create --interactive', 'Cathay BDK 互動式問答') + .option('interactive', { type: 'boolean', description: '是否使用 Cathay BDK 互動式問答', alias: 'i' }) +} + +export const handler = async (argv: Arguments) => { + const wallet = new Wallet() + + let type: WalletType + + if (argv.interactive) { + const walletOption = [ + { title: 'ethereum', value: WalletType.ETHEREUM }, + ] + + const { walletType } = await prompts([{ + type: 'select', + name: 'walletType', + message: 'What type of wallet you want to create?', + choices: walletOption, + }], { onCancel }) + type = walletType + } else { + type = WalletType.ETHEREUM + } + + const walletCreateConfig: WalletCreateType = { + type: type, + } + + const spinner = ora('Wallet Create ...').start() + const result = await wallet.create(walletCreateConfig) + spinner.succeed(`Wallet Create Result:\n ${result}`) + spinner.succeed('Wallet Create Successfully!') +} diff --git a/src/wallet/model/type/wallet.type.ts b/src/wallet/model/type/wallet.type.ts new file mode 100644 index 00000000..b1dbc3cf --- /dev/null +++ b/src/wallet/model/type/wallet.type.ts @@ -0,0 +1,9 @@ +export enum WalletType { + ETHEREUM = 'ethereum' +} +/** + * @requires ethereum - [string] ethereum +*/ +export interface WalletCreateType { + type: WalletType +} diff --git a/src/wallet/service/wallet.ts b/src/wallet/service/wallet.ts new file mode 100644 index 00000000..f9d84741 --- /dev/null +++ b/src/wallet/service/wallet.ts @@ -0,0 +1,34 @@ + +import { ethers } from 'ethers' +import { WalletCreateType, WalletType } from '../model/type/wallet.type' + +export default class Wallet { + /** + * @description 建立 quorum network + */ + public create (walletCreateConfig: WalletCreateType) { + const { address, privateKey } = this.createWalletAddress(walletCreateConfig.type) + const walletAddress = address + const result = `🔑 Your ${walletCreateConfig.type} wallet address: 0x${walletAddress}\n 🔑 Wallet private key: ${privateKey}` + return result + } + + /** @ignore */ + public createWalletAddress (type: WalletType) { + let nodekey: ethers.Wallet + let privateKey: string + let publicKey: string + let address: string + + switch (type) { + case WalletType.ETHEREUM: + nodekey = ethers.Wallet.createRandom() + privateKey = nodekey.privateKey.replace(/^0x/, '') + publicKey = nodekey.publicKey.replace(/^0x04/, '') + address = nodekey.address.replace(/^0x/, '').toLowerCase() + break + } + + return { privateKey, publicKey, address } + } +} diff --git a/src/wallet/wallet.ts b/src/wallet/wallet.ts new file mode 100644 index 00000000..447f7503 --- /dev/null +++ b/src/wallet/wallet.ts @@ -0,0 +1,11 @@ +import { Argv } from 'yargs' + +export const command = 'wallet' + +export const desc = '管理 Wallet 的指令' + +export const builder = (yargs: Argv) => { + return yargs.commandDir('../wallet/command').demandCommand() +} + +export const handler = {} diff --git a/test/quorum/service/network.test.ts b/test/quorum/service/network.test.ts index cc3dc867..479242bf 100644 --- a/test/quorum/service/network.test.ts +++ b/test/quorum/service/network.test.ts @@ -8,6 +8,8 @@ import fs from 'fs' import { resolve } from 'path' import { sleep, TimeLimitError } from '../../../src/util' import { JoinNodeType } from '../../../src/quorum/model/type/network.type' +import Wallet from '../../../src/wallet/service/wallet' +import { WalletType } from '../../../src/wallet/model/type/wallet.type' describe.skip('Quorum.Network.Service', function () { this.timeout(1000000) @@ -18,7 +20,8 @@ describe.skip('Quorum.Network.Service', function () { const filePath = resolve(`${bdkPath}/bdk-quorum-network`) const network = new Network(config) - const { address } = network.createWalletAddress() + const wallet = new Wallet() + const { address } = wallet.createWalletAddress(WalletType.ETHEREUM) const networkCreateConfig = { validatorNumber: 2, memberNumber: 2, diff --git a/test/wallet/service/create.test.ts b/test/wallet/service/create.test.ts new file mode 100644 index 00000000..c041f442 --- /dev/null +++ b/test/wallet/service/create.test.ts @@ -0,0 +1,22 @@ +/* global describe, it */ +import assert from 'assert' +import Wallet from '../../../src/wallet/service/wallet' +import { WalletCreateType, WalletType } from '../../../src/wallet/model/type/wallet.type' + +// write a test for the wallet class +describe('Wallet.Create', function () { + this.timeout(1000) + const wallet = new Wallet() + + // create a new ethereum wallet + describe('Wallet.Create', () => { + it('should create a ethereum wallet with address and private key', () => { + const WalletCreateType: WalletCreateType = { + type: WalletType.ETHEREUM, + } + const result = wallet.create(WalletCreateType) + + assert(result.length > 0, 'Wallet create error') + }) + }) +})