diff --git a/examples/testNode.js b/examples/testNode.js index 8924f5f..6ac4d92 100644 --- a/examples/testNode.js +++ b/examples/testNode.js @@ -1,4 +1,4 @@ -const { Account } = require('../packages/harmony-account/lib/index.js'); +const { Account, Wallet } = require('../packages/harmony-account/lib/index.js'); const { getAddressFromPublicKey, } = require('../packages/harmony-crypto/lib/index.js'); @@ -13,21 +13,48 @@ const { const importKey = '0x87b3ec80f36f9553fb63624d0805d87cfe461145c7be972d23db95fb1a53b1e7'; +const importKey2 = + '0x87b3ec80f36f9553fb63624d0805d87cfe461145c7be972d23db95fb1a53b1e7'; + +const d = new Wallet(); + +const e = d.addByPrivateKey(importKey); +const f = d.addByPrivateKey(importKey2); + +const testWallet = async () => { + await d.encryptAccount(e.address, '123'); + console.log(d.accounts); + console.log(d.getAccount(e.address)); + + console.log('---------'); + await d.decryptAccount(e.address, '123'); + console.log(d.accounts); + console.log(d.getAccount(e.address)); + + const mne = d.generateMnemonic(); + const g = d.addByMnemonic(mne, 0); + await d.encryptAccount(g.address, '123'); + console.log('---------'); + console.log(d.accounts); + console.log(d.getAccount(g.address)); +}; + +testWallet(); -const c = Account.add(importKey); +// const c = Account.add(importKey); -// console.log(isPrivateKey(importKey)); +// // console.log(isPrivateKey(importKey)); -c.addShard('newShard'); +// c.addShard('newShard'); -console.log(c.getShardsCount); +// console.log(c.getShardsCount); -c.toFile('123').then((f) => { - console.log('---- encrypting ----'); - console.log(f); +// c.toFile('123').then((f) => { +// console.log('---- encrypting ----'); +// console.log(f); - c.fromFile(f, '123').then((a) => { - console.log('--- decrypting and add to Account ----'); - console.log(a); - }); -}); +// c.fromFile(f, '123').then((a) => { +// console.log('--- decrypting and add to Account ----'); +// console.log(a); +// }); +// }); diff --git a/package.json b/package.json index cf77e07..3718a03 100644 --- a/package.json +++ b/package.json @@ -44,11 +44,13 @@ "@babel/preset-typescript": "^7.0.0-beta.56", "@babel/runtime": "^7.0.0-beta.56", "@trust/webcrypto": "^0.9.2", + "@types/bip39": "^2.4.2", "@types/bn.js": "^4.11.3", "@types/camelcase": "^4.1.0", "@types/fancy-log": "^1.3.0", "@types/glob": "^7.1.1", "@types/glob-parent": "^3.1.0", + "@types/hdkey": "^0.7.0", "@types/jest": "^23.3.1", "@types/jest-json-schema": "^1.2.0", "@types/node": "^10.5.6", diff --git a/packages/harmony-account/src/account.ts b/packages/harmony-account/src/account.ts index 925835a..218ed1b 100644 --- a/packages/harmony-account/src/account.ts +++ b/packages/harmony-account/src/account.ts @@ -77,6 +77,7 @@ class Account { async toFile(password: string, options?: EncryptOptions): Promise { if (this.privateKey && isPrivateKey(this.privateKey)) { const file = await encrypt(this.privateKey, password, options); + this.privateKey = file; return file; } else { throw new Error('Encryption failed because PrivateKey is not correct'); @@ -91,7 +92,7 @@ class Account { const file: Keystore = JSON.parse(keyStore); const decyptedPrivateKey = await decrypt(file, password); if (isPrivateKey(decyptedPrivateKey)) { - return Account.add(decyptedPrivateKey); + return this._import(decyptedPrivateKey); } else { throw new Error('decrypted failed'); } @@ -109,6 +110,20 @@ class Account { return ''; } + /** + * @function updateShards + * @return {Promise} {description} + */ + async updateShards(): Promise { + return ''; + } + /** + * @function signTransaction + * @return {Promise} sign transaction + */ + async signTransaction(): Promise { + console.log('sign transaction'); + } /** * @function _new private method create Account * @return {Account} Account instance diff --git a/packages/harmony-account/src/index.ts b/packages/harmony-account/src/index.ts index 362a768..73927b0 100644 --- a/packages/harmony-account/src/index.ts +++ b/packages/harmony-account/src/index.ts @@ -1 +1,2 @@ export * from './account'; +export * from './wallet'; diff --git a/packages/harmony-account/src/wallet.ts b/packages/harmony-account/src/wallet.ts index e69de29..6f2c738 100644 --- a/packages/harmony-account/src/wallet.ts +++ b/packages/harmony-account/src/wallet.ts @@ -0,0 +1,104 @@ +import { bip39, hdkey, EncryptOptions } from '@harmony/crypto'; +import { isPrivateKey } from '@harmony/utils'; +import { Account } from './account'; + +class Wallet { + private accountMap: Map = new Map(); + get accounts(): string[] { + return [...this.accountMap.keys()]; + } + generateMnemonic(): string { + return bip39.generateMnemonic(); + } + addByMnemonic(phrase: string, index: number = 0) { + if (!this.isValidMnemonic(phrase)) { + throw new Error(`Invalid mnemonic phrase: ${phrase}`); + } + const seed = bip39.mnemonicToSeed(phrase); + const hdKey = hdkey.fromMasterSeed(seed); + const childKey = hdKey.derive(`m/44'/313'/0'/0/${index}`); + const privateKey = childKey.privateKey.toString('hex'); + return this.addByPrivateKey(privateKey); + } + addByPrivateKey(privateKey: string): Account { + try { + const newAcc = Account.add(privateKey); + if (newAcc.address) { + this.accountMap.set(newAcc.address, newAcc); + return newAcc; + } else { + throw new Error('add account failed'); + } + } catch (error) { + throw error; + } + } + async encryptAccount( + address: string, + password: string, + options?: EncryptOptions, + ): Promise { + try { + const foundAcc = this.getAccount(address); + if ( + foundAcc && + foundAcc.privateKey && + isPrivateKey(foundAcc.privateKey) + ) { + await foundAcc.toFile(password, options); + return foundAcc; + } else if ( + foundAcc && + foundAcc.privateKey && + !isPrivateKey(foundAcc.privateKey) + ) { + return foundAcc; + } else { + throw new Error('encrypt account failed'); + } + } catch (error) { + throw error; + } + } + + async decryptAccount(address: string, password: string): Promise { + try { + const foundAcc = this.getAccount(address); + if ( + foundAcc && + foundAcc.privateKey && + !isPrivateKey(foundAcc.privateKey) + ) { + await foundAcc.fromFile(foundAcc.privateKey, password); + return foundAcc; + } else if ( + foundAcc && + foundAcc.privateKey && + isPrivateKey(foundAcc.privateKey) + ) { + return foundAcc; + } else { + throw new Error('decrypt account failed'); + } + } catch (error) { + throw error; + } + } + + getAccount(address: string): Account | undefined { + return this.accountMap.get(address); + } + + removeAccount(address: string): void { + this.accountMap.delete(address); + } + + private isValidMnemonic(phrase: string): boolean { + if (phrase.trim().split(/\s+/g).length < 12) { + return false; + } + return bip39.validateMnemonic(phrase); + } +} + +export { Wallet }; diff --git a/packages/harmony-crypto/src/index.ts b/packages/harmony-crypto/src/index.ts index 2143720..0eb5021 100644 --- a/packages/harmony-crypto/src/index.ts +++ b/packages/harmony-crypto/src/index.ts @@ -1,6 +1,11 @@ +import hdkey from 'hdkey'; +import bip39 from 'bip39'; + export * from './random'; export * from './keyTool'; export * from './keystore'; // export types export * from './types'; + +export { hdkey, bip39 };