From 4a4b8d128c7280095415ce7480660c7498bc5001 Mon Sep 17 00:00:00 2001 From: Jasmine/kimjimin Date: Tue, 26 May 2020 10:25:32 +0900 Subject: [PATCH 1/5] Returns an array of address after generating in KeyringContainer --- packages/caver-wallet/src/keyringContainer.js | 5 ++++- test/packages/caver.wallet.js | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/caver-wallet/src/keyringContainer.js b/packages/caver-wallet/src/keyringContainer.js index bd7ed2d7..fa7042d0 100644 --- a/packages/caver-wallet/src/keyringContainer.js +++ b/packages/caver-wallet/src/keyringContainer.js @@ -53,11 +53,14 @@ class KeyringContainer { * * @param {number} numberOfKeyrings The number of accounts to create. * @param {string} [entropy] A random string to increase entropy. If undefined, a random string will be generated using randomHex. + * @return {Array.} */ generate(numberOfKeyrings, entropy) { + const addresses = [] for (let i = 0; i < numberOfKeyrings; ++i) { - this.add(Keyring.generate(entropy)) + addresses.push(this.add(Keyring.generate(entropy)).address) } + return addresses } /** diff --git a/test/packages/caver.wallet.js b/test/packages/caver.wallet.js index f6ca175d..7dded73d 100644 --- a/test/packages/caver.wallet.js +++ b/test/packages/caver.wallet.js @@ -89,8 +89,13 @@ describe('wallet.generate', () => { it('should generate keyring instances and add to in-memory wallet', () => { const addSpy = sinon.spy(caver.wallet.keyringContainer, 'add') - caver.wallet.generate(10) + const addresses = caver.wallet.generate(10) + for (const address of addresses) { + expect(caver.utils.isAddress(address)).to.be.true + } + + expect(addresses.length).to.equal(10) expect(caver.wallet.length).to.equal(10) expect(addSpy).to.have.been.callCount(10) }) @@ -101,8 +106,13 @@ describe('wallet.generate', () => { const addSpy = sinon.spy(caver.wallet.keyringContainer, 'add') const entropy = caver.utils.randomHex(32) - caver.wallet.generate(10, entropy) + const addresses = caver.wallet.generate(10, entropy) + + for (const address of addresses) { + expect(caver.utils.isAddress(address)).to.be.true + } + expect(addresses.length).to.equal(10) expect(caver.wallet.length).to.equal(10) expect(addSpy).to.have.been.callCount(10) }) From 727ade1c5fc4ae852e8cffcae4a1be7e33e6f1d0 Mon Sep 17 00:00:00 2001 From: Jasmine/kimjimin Date: Tue, 26 May 2020 10:40:01 +0900 Subject: [PATCH 2/5] Fixed scrypt-shim CI fail --- package.json | 2 +- packages/caver-klay/caver-klay-accounts/src/index.js | 2 +- packages/caver-wallet/src/keyring/keyring.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index fab8df62..ea70a2e4 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "oboe": "2.1.3", "request": "2.87.0", "requestretry": "^2.0.2", - "scrypt-shim": "github:web3-js/scrypt-shim", + "@web3-js/scrypt-shim": "^0.1.0", "semver": "6.2.0", "utf8": "2.1.1", "uuid": "^3.0.0", diff --git a/packages/caver-klay/caver-klay-accounts/src/index.js b/packages/caver-klay/caver-klay-accounts/src/index.js index 29af1bb7..a9339ccf 100644 --- a/packages/caver-klay/caver-klay-accounts/src/index.js +++ b/packages/caver-klay/caver-klay-accounts/src/index.js @@ -35,7 +35,7 @@ const Bytes = require('eth-lib/lib/bytes') const cryp = typeof global === 'undefined' ? require('crypto-browserify') : require('crypto') const uuid = require('uuid') const elliptic = require('elliptic') -const scrypt = require('scrypt-shim') +const scrypt = require('@web3-js/scrypt-shim') const utils = require('../../../caver-utils') const helpers = require('../../../caver-core-helpers') diff --git a/packages/caver-wallet/src/keyring/keyring.js b/packages/caver-wallet/src/keyring/keyring.js index 2ff6e23a..71199df9 100644 --- a/packages/caver-wallet/src/keyring/keyring.js +++ b/packages/caver-wallet/src/keyring/keyring.js @@ -17,7 +17,7 @@ */ const _ = require('lodash') -const scrypt = require('scrypt-shim') +const scrypt = require('@web3-js/scrypt-shim') const uuid = require('uuid') const cryp = typeof global === 'undefined' ? require('crypto-browserify') : require('crypto') const AccountLib = require('eth-lib/lib/account') From b7340df291b733bbdf0372329f2154dac8abe5d9 Mon Sep 17 00:00:00 2001 From: Jasmine/kimjimin Date: Tue, 26 May 2020 13:57:41 +0900 Subject: [PATCH 3/5] Modified wallet layer classes and implement generate function for each key type --- index.js | 6 +- packages/caver-wallet/src/index.js | 292 +++++++++++++++-- packages/caver-wallet/src/keyring/keyring.js | 61 ++++ packages/caver-wallet/src/keyringContainer.js | 303 ------------------ test/packages/caver.wallet.js | 50 +-- test/packages/caver.wallet.keyring.js | 216 ++++++++++--- test/packages/utils.js | 21 +- 7 files changed, 505 insertions(+), 444 deletions(-) delete mode 100644 packages/caver-wallet/src/keyringContainer.js diff --git a/index.js b/index.js index 0579f2ca..c989cc82 100644 --- a/index.js +++ b/index.js @@ -34,7 +34,8 @@ global.rootRequire = name => require(`${__dirname}/packages/${name}/src/index.js const { packageInit, providers } = require('./packages/caver-core') const Klay = require('./packages/caver-klay') const Account = require('./packages/caver-account') -const Wallet = require('./packages/caver-wallet') +const KeyringContainer = require('./packages/caver-wallet') +const Keyring = require('./packages/caver-wallet/src/keyring/keyring') const Transaction = require('./packages/caver-transaction') const RPC = require('./packages/caver-rpc') @@ -60,7 +61,8 @@ function Caver(provider, net) { this.Method = Method this.account = Account - this.wallet = new Wallet() + this.wallet = new KeyringContainer() + this.wallet.keyring = Keyring this.transaction = Transaction diff --git a/packages/caver-wallet/src/index.js b/packages/caver-wallet/src/index.js index fb9081eb..fa7042d0 100644 --- a/packages/caver-wallet/src/index.js +++ b/packages/caver-wallet/src/index.js @@ -16,56 +16,288 @@ along with the caver-js. If not, see . */ -const AccountLib = require('eth-lib/lib/account') -const KeyringContainer = require('./keyringContainer') +const _ = require('lodash') const Keyring = require('./keyring/keyring') +const TransactionHasher = require('../../caver-transaction/src/transactionHasher/transactionHasher') +const { KEY_ROLE } = require('./keyring/keyringHelper') const utils = require('../../caver-utils/src') /** - * representing a Wallet class in caver-wallet package. - * This Wallet class is a wrapper for KeyringContainer. + * representing a Keyring container which manages keyrings. * @class */ -class Wallet { +class KeyringContainer { + /** + * creates a keyringContainer. + * @param {Array.} keyrings - The keyrings to be managed in KeyringContainer. + */ constructor(keyrings) { - this.keyringContainer = new KeyringContainer(keyrings) - this.keyring = Keyring - - // Bind methods of KeyringContainer to Wallet - this.generate = this.keyringContainer.generate.bind(this.keyringContainer) - this.newKeyring = this.keyringContainer.newKeyring.bind(this.keyringContainer) - this.updateKeyring = this.keyringContainer.updateKeyring.bind(this.keyringContainer) - this.getKeyring = this.keyringContainer.getKeyring.bind(this.keyringContainer) - - this.add = this.keyringContainer.add.bind(this.keyringContainer) - this.remove = this.keyringContainer.remove.bind(this.keyringContainer) - - this.signMessage = this.keyringContainer.signMessage.bind(this.keyringContainer) - this.signWithKey = this.keyringContainer.signWithKey.bind(this.keyringContainer) - this.signWithKeys = this.keyringContainer.signWithKeys.bind(this.keyringContainer) - this.signFeePayerWithKey = this.keyringContainer.signFeePayerWithKey.bind(this.keyringContainer) - this.signFeePayerWithKeys = this.keyringContainer.signFeePayerWithKeys.bind(this.keyringContainer) + keyrings = keyrings || [] + this._addressKeyringMap = new Map() + + // add keyrings to keyringContainer + for (const keyring of keyrings) { + this.add(keyring) + } } /** * @type {number} */ get length() { - return this.keyringContainer.length + return this._addressKeyringMap.size + } + + /** + * generates keyrings in the keyringContainer with randomly generated key pairs. + * + * @param {number} numberOfKeyrings The number of accounts to create. + * @param {string} [entropy] A random string to increase entropy. If undefined, a random string will be generated using randomHex. + * @return {Array.} + */ + generate(numberOfKeyrings, entropy) { + const addresses = [] + for (let i = 0; i < numberOfKeyrings; ++i) { + addresses.push(this.add(Keyring.generate(entropy)).address) + } + return addresses + } + + /** + * creates a keyring instance with given parameters and adds it to the keyringContainer. + * KeyringContainer manages Keyring instance using Map which has address as key value. + * + * @param {string} address The address of the keyring. + * @param {string|Array.|Array.>} key Private key string(s) to use in keyring. If different keys are used for each role, key must be defined as a two-dimensional array. + * @return {Keyring} + */ + newKeyring(address, key) { + // The format of key parameter can be + // 1. single private key string => `0x{private key}` + // 2. multiple private key string =>[`0x{private key}`, `0x{private key}`, ...] + // 3. role based private keys => [[`0x{private key}`, `0x{private key}`, ...], [], [`0x{private key}`]] + + let keyring + + if (_.isString(key)) keyring = Keyring.createWithSingleKey(address, key) + + if (_.isArray(key)) { + if (key.length === 0) throw new Error(`Insufficient private key information: Empty array`) + if (_.isArray(key[0])) { + keyring = Keyring.createWithRoleBasedKey(address, key) + } else { + keyring = Keyring.createWithMultipleKey(address, key) + } + } + + if (!(keyring instanceof Keyring)) throw new Error(`Unsupported type value: ${key} (type:${typeof key})`) + + return this.add(keyring) + } + + /** + * updates the keyring inside the keyringContainer. + * Query the keyring to be updated from keyringContainer with the keyring's address, + * and an error occurs when the keyring is not found in the keyringContainer. + * + * @param {Keyring} keyring The keyring with new key. + * @return {Keyring} + */ + updateKeyring(keyring) { + const founded = this._addressKeyringMap.get(keyring.address.toLowerCase()) + if (founded === undefined) throw new Error(`Failed to find keyring to update`) + + founded.keys = keyring.copy().keys + return founded } /** - * generates a private key string + * Get the keyring in container corresponding to the address * - * `caver.wallet.generatePrivateKey()` + * @param {string} address The address of keyring to query. + * @return {Keyring} + */ + getKeyring(address) { + if (!utils.isAddress(address)) + throw new Error( + `Invalid address ${address}. To get keyring from wallet, you need to pass a valid address string as a parameter.` + ) + + const founded = this._addressKeyringMap.get(address.toLowerCase()) + + return founded + } + + /** + * adds a keyring to the keyringContainer. + * + * @param {Keyring} keyring A keyring instance to add to keyringContainer. + * @return {Keyring} + */ + add(keyring) { + if (this._addressKeyringMap.get(keyring.address.toLowerCase()) !== undefined) + throw new Error(`Duplicate Account ${keyring.address}. Please use updateKeyring() instead.`) + + const keyringToAdd = keyring.copy() + + this._addressKeyringMap.set(keyringToAdd.address.toLowerCase(), keyringToAdd) + + return keyringToAdd + } + + /** + * deletes the keyring that associates with the given address from keyringContainer. * - * @param {string} entropy A random string to increase entropy. + * @param {string} address An address of the keyring to be deleted in keyringContainer. + * @return {boolean} + */ + remove(address) { + let keyringToRemove + if (utils.isAddress(address)) { + keyringToRemove = this.getKeyring(address) + } else { + throw new Error(`To remove the keyring, the first parameter should be an address string.`) + } + + if (keyringToRemove === undefined) return false + + // deallocate keyring object created for keyringContainer + keyringToRemove.keys = null + this._addressKeyringMap.delete(keyringToRemove.address.toLowerCase()) + + return true + } + + /** + * signs with data and returns the result object that includes `signature`, `message` and `messageHash` + * + * @param {string} address An address of keyring in keyringContainer. + * @param {string} data The data string to sign. + * @param {number} [role] A number indicating the role of the key. You can use `caver.wallet.keyring.role`. + * @param {number} [index] An index of key to use for signing. + * @return {object} + */ + signMessage(address, data, role, index) { + const keyring = this.getKeyring(address) + if (keyring === undefined) throw new Error(`Failed to find keyring from wallet with ${address}`) + return keyring.signMessage(data, role, index) + } + + /** + * signs the transaction using one key and return the transactionHash + * + * @param {string} address An address of keyring in keyringContainer. + * @param {Transaction} transaction A transaction object. + * @param {number} [index] An index of key to use for signing. + * @param {function} [hasher] A function to return hash of transaction. In order to use a custom hasher, the index must be defined. + * @return {Transaction} + */ + async signWithKey(address, transaction, index = 0, hasher = TransactionHasher.getHashForSignature) { + if (!transaction.from || transaction.from === '0x') transaction.from = address + if (transaction.from.toLowerCase() !== address.toLowerCase()) + throw new Error( + `transaction.from ${transaction.from.toLowerCase()} is different from the given address ${address.toLowerCase()}.` + ) + + // User parameter input cases + // (address transaction) / (address transaction index) / (address transaction index hasher) + if (_.isFunction(index)) throw new Error(`In order to pass a custom hasher, use the third parameter.`) + + await transaction.fillTransaction() + const hash = hasher(transaction) + const role = determineRoleToSign(transaction) + + const keyring = this.getKeyring(address) + if (keyring === undefined) throw new Error(`Failed to find keyring from wallet with ${address}`) + const sig = keyring.signWithKey(hash, transaction.chainId, role, index) + + transaction.appendSignatures(sig) + + return transaction + } + + /** + * signs the transaction using keys and return the transactionHash + * + * @param {string} address An address of keyring in keyringContainer. + * @param {Transaction} transaction A transaction object. + * @param {function} [hasher] A function to return hash of transaction. + * @return {Transaction} + */ + async signWithKeys(address, transaction, hasher = TransactionHasher.getHashForSignature) { + if (!transaction.from || transaction.from === '0x') transaction.from = address + if (transaction.from.toLowerCase() !== address.toLowerCase()) + throw new Error( + `transaction.from ${transaction.from.toLowerCase()} is different from the given address ${address.toLowerCase()}.` + ) + + await transaction.fillTransaction() + const hash = hasher(transaction) + const role = determineRoleToSign(transaction) + + const keyring = this.getKeyring(address) + if (keyring === undefined) throw new Error(`Failed to find the keyring from the wallet with the given address: ${address}`) + const sigs = keyring.signWithKeys(hash, transaction.chainId, role) + + transaction.appendSignatures(sigs) + + return transaction + } + + /** + * signs the transaction as a fee payer using one key and return the transactionHash + * + * @param {string} address An address of keyring in keyringContainer. + * @param {Transaction} transaction A transaction object. This should be `FEE_DELEGATED` type. + * @param {number} [index] An index of key to use for signing. + * @param {function} [hasher] A function to return hash of transaction. In order to use a custom hasher, the index must be defined. * @return {string} */ - // eslint-disable-next-line class-methods-use-this - generatePrivateKey(entropy) { - return AccountLib.create(entropy || utils.randomHex(32)).privateKey + async signFeePayerWithKey(address, transaction, index = 0, hasher = TransactionHasher.getHashForFeePayerSignature) { + // User parameter input cases + // (address transaction) / (address transaction index) / (address transaction index hasher) + if (_.isFunction(index)) throw new Error(`In order to pass a custom hasher, use the third parameter.`) + + if (!transaction.feePayer || transaction.feePayer === '0x') transaction.feePayer = address + + await transaction.fillTransaction() + const hash = hasher(transaction) + + const keyring = this.getKeyring(address) + if (keyring === undefined) throw new Error(`Failed to find keyring from wallet with ${address}`) + const sig = keyring.signWithKey(hash, transaction.chainId, KEY_ROLE.RoleFeePayerKey, index) + + transaction.appendFeePayerSignatures(sig) + + return hash } + + /** + * signs the transaction as a fee payer using keys and return the transactionHash + * + * @param {string} address An address of keyring in keyringContainer. + * @param {Transaction} transaction A transaction object. This should be `FEE_DELEGATED` type. + * @param {function} [hasher] A function to return hash of transaction. + * @return {string} + */ + async signFeePayerWithKeys(address, transaction, hasher = TransactionHasher.getHashForFeePayerSignature) { + if (!transaction.feePayer || transaction.feePayer === '0x') transaction.feePayer = address + + await transaction.fillTransaction() + const hash = hasher(transaction) + + const keyring = this.getKeyring(address) + if (keyring === undefined) throw new Error(`Failed to find keyring from wallet with ${address}`) + const sigs = keyring.signWithKeys(hash, transaction.chainId, KEY_ROLE.RoleFeePayerKey) + + transaction.appendFeePayerSignatures(sigs) + + return hash + } +} + +function determineRoleToSign(tx) { + return tx.type.includes('ACCOUNT_UPDATE') ? KEY_ROLE.RoleAccountUpdateKey : KEY_ROLE.RoleTransactionKey } -module.exports = Wallet +module.exports = KeyringContainer diff --git a/packages/caver-wallet/src/keyring/keyring.js b/packages/caver-wallet/src/keyring/keyring.js index 71199df9..750ce0ca 100644 --- a/packages/caver-wallet/src/keyring/keyring.js +++ b/packages/caver-wallet/src/keyring/keyring.js @@ -49,6 +49,67 @@ class Keyring { return Keyring.createWithSingleKey(random.address, random.privateKey) } + /** + * generates a decoupled keyring instance + * + * `caver.wallet.keyring.generateSingleKey()` + * + * @param {string} entropy A random string to increase entropy. + * @return {String} + */ + static generateSingleKey(entropy) { + return AccountLib.create(entropy || utils.randomHex(32)).privateKey + } + + /** + * generates an keyring instance with multiple keys + * + * `caver.wallet.keyring.generateMultipleKeys()` + * + * @param {number} num A length of keys. + * @param {string} entropy A random string to increase entropy. + * @return {Keyring} + */ + static generateMultipleKeys(num, entropy) { + if (num === undefined || !_.isNumber(num) || _.isString(num)) { + throw new Error(`To generate random multiple private keys, the number of keys should be defined.`) + } + + const randomKeys = [] + for (let i = 0; i < num; i++) { + randomKeys.push(AccountLib.create(entropy || utils.randomHex(32)).privateKey) + } + return randomKeys + } + + /** + * generates an keyring instance with role-based keys + * + * `caver.wallet.keyring.generateRoleBasedKeys()` + * + * @param {Array.} numArr An array containing the number of keys for each role. + * @param {string} entropy A random string to increase entropy. + * @return {Keyring} + */ + static generateRoleBasedKeys(numArr, entropy) { + if (numArr === undefined || !_.isArray(numArr) || _.isString(numArr)) { + throw new Error( + `To generate random role-based private keys, an array containing the number of keys for each role should be defined.` + ) + } + if (numArr.length > KEY_ROLE.RoleLast) { + throw new Error(`Unsupported role. The length of array should be less than ${KEY_ROLE.RoleLast}.`) + } + + const randomKeys = [[], [], []] + for (let i = 0; i < numArr.length; i++) { + for (let j = 0; j < numArr[i]; j++) { + randomKeys[i].push(AccountLib.create(entropy || utils.randomHex(32)).privateKey) + } + } + return randomKeys + } + /** * creates a keyring instance with parameters * diff --git a/packages/caver-wallet/src/keyringContainer.js b/packages/caver-wallet/src/keyringContainer.js deleted file mode 100644 index fa7042d0..00000000 --- a/packages/caver-wallet/src/keyringContainer.js +++ /dev/null @@ -1,303 +0,0 @@ -/* - Copyright 2020 The caver-js Authors - This file is part of the caver-js library. - - The caver-js library is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - The caver-js library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with the caver-js. If not, see . -*/ - -const _ = require('lodash') -const Keyring = require('./keyring/keyring') -const TransactionHasher = require('../../caver-transaction/src/transactionHasher/transactionHasher') -const { KEY_ROLE } = require('./keyring/keyringHelper') -const utils = require('../../caver-utils/src') - -/** - * representing a Keyring container which manages keyrings. - * @class - */ -class KeyringContainer { - /** - * creates a keyringContainer. - * @param {Array.} keyrings - The keyrings to be managed in KeyringContainer. - */ - constructor(keyrings) { - keyrings = keyrings || [] - this._addressKeyringMap = new Map() - - // add keyrings to keyringContainer - for (const keyring of keyrings) { - this.add(keyring) - } - } - - /** - * @type {number} - */ - get length() { - return this._addressKeyringMap.size - } - - /** - * generates keyrings in the keyringContainer with randomly generated key pairs. - * - * @param {number} numberOfKeyrings The number of accounts to create. - * @param {string} [entropy] A random string to increase entropy. If undefined, a random string will be generated using randomHex. - * @return {Array.} - */ - generate(numberOfKeyrings, entropy) { - const addresses = [] - for (let i = 0; i < numberOfKeyrings; ++i) { - addresses.push(this.add(Keyring.generate(entropy)).address) - } - return addresses - } - - /** - * creates a keyring instance with given parameters and adds it to the keyringContainer. - * KeyringContainer manages Keyring instance using Map which has address as key value. - * - * @param {string} address The address of the keyring. - * @param {string|Array.|Array.>} key Private key string(s) to use in keyring. If different keys are used for each role, key must be defined as a two-dimensional array. - * @return {Keyring} - */ - newKeyring(address, key) { - // The format of key parameter can be - // 1. single private key string => `0x{private key}` - // 2. multiple private key string =>[`0x{private key}`, `0x{private key}`, ...] - // 3. role based private keys => [[`0x{private key}`, `0x{private key}`, ...], [], [`0x{private key}`]] - - let keyring - - if (_.isString(key)) keyring = Keyring.createWithSingleKey(address, key) - - if (_.isArray(key)) { - if (key.length === 0) throw new Error(`Insufficient private key information: Empty array`) - if (_.isArray(key[0])) { - keyring = Keyring.createWithRoleBasedKey(address, key) - } else { - keyring = Keyring.createWithMultipleKey(address, key) - } - } - - if (!(keyring instanceof Keyring)) throw new Error(`Unsupported type value: ${key} (type:${typeof key})`) - - return this.add(keyring) - } - - /** - * updates the keyring inside the keyringContainer. - * Query the keyring to be updated from keyringContainer with the keyring's address, - * and an error occurs when the keyring is not found in the keyringContainer. - * - * @param {Keyring} keyring The keyring with new key. - * @return {Keyring} - */ - updateKeyring(keyring) { - const founded = this._addressKeyringMap.get(keyring.address.toLowerCase()) - if (founded === undefined) throw new Error(`Failed to find keyring to update`) - - founded.keys = keyring.copy().keys - return founded - } - - /** - * Get the keyring in container corresponding to the address - * - * @param {string} address The address of keyring to query. - * @return {Keyring} - */ - getKeyring(address) { - if (!utils.isAddress(address)) - throw new Error( - `Invalid address ${address}. To get keyring from wallet, you need to pass a valid address string as a parameter.` - ) - - const founded = this._addressKeyringMap.get(address.toLowerCase()) - - return founded - } - - /** - * adds a keyring to the keyringContainer. - * - * @param {Keyring} keyring A keyring instance to add to keyringContainer. - * @return {Keyring} - */ - add(keyring) { - if (this._addressKeyringMap.get(keyring.address.toLowerCase()) !== undefined) - throw new Error(`Duplicate Account ${keyring.address}. Please use updateKeyring() instead.`) - - const keyringToAdd = keyring.copy() - - this._addressKeyringMap.set(keyringToAdd.address.toLowerCase(), keyringToAdd) - - return keyringToAdd - } - - /** - * deletes the keyring that associates with the given address from keyringContainer. - * - * @param {string} address An address of the keyring to be deleted in keyringContainer. - * @return {boolean} - */ - remove(address) { - let keyringToRemove - if (utils.isAddress(address)) { - keyringToRemove = this.getKeyring(address) - } else { - throw new Error(`To remove the keyring, the first parameter should be an address string.`) - } - - if (keyringToRemove === undefined) return false - - // deallocate keyring object created for keyringContainer - keyringToRemove.keys = null - this._addressKeyringMap.delete(keyringToRemove.address.toLowerCase()) - - return true - } - - /** - * signs with data and returns the result object that includes `signature`, `message` and `messageHash` - * - * @param {string} address An address of keyring in keyringContainer. - * @param {string} data The data string to sign. - * @param {number} [role] A number indicating the role of the key. You can use `caver.wallet.keyring.role`. - * @param {number} [index] An index of key to use for signing. - * @return {object} - */ - signMessage(address, data, role, index) { - const keyring = this.getKeyring(address) - if (keyring === undefined) throw new Error(`Failed to find keyring from wallet with ${address}`) - return keyring.signMessage(data, role, index) - } - - /** - * signs the transaction using one key and return the transactionHash - * - * @param {string} address An address of keyring in keyringContainer. - * @param {Transaction} transaction A transaction object. - * @param {number} [index] An index of key to use for signing. - * @param {function} [hasher] A function to return hash of transaction. In order to use a custom hasher, the index must be defined. - * @return {Transaction} - */ - async signWithKey(address, transaction, index = 0, hasher = TransactionHasher.getHashForSignature) { - if (!transaction.from || transaction.from === '0x') transaction.from = address - if (transaction.from.toLowerCase() !== address.toLowerCase()) - throw new Error( - `transaction.from ${transaction.from.toLowerCase()} is different from the given address ${address.toLowerCase()}.` - ) - - // User parameter input cases - // (address transaction) / (address transaction index) / (address transaction index hasher) - if (_.isFunction(index)) throw new Error(`In order to pass a custom hasher, use the third parameter.`) - - await transaction.fillTransaction() - const hash = hasher(transaction) - const role = determineRoleToSign(transaction) - - const keyring = this.getKeyring(address) - if (keyring === undefined) throw new Error(`Failed to find keyring from wallet with ${address}`) - const sig = keyring.signWithKey(hash, transaction.chainId, role, index) - - transaction.appendSignatures(sig) - - return transaction - } - - /** - * signs the transaction using keys and return the transactionHash - * - * @param {string} address An address of keyring in keyringContainer. - * @param {Transaction} transaction A transaction object. - * @param {function} [hasher] A function to return hash of transaction. - * @return {Transaction} - */ - async signWithKeys(address, transaction, hasher = TransactionHasher.getHashForSignature) { - if (!transaction.from || transaction.from === '0x') transaction.from = address - if (transaction.from.toLowerCase() !== address.toLowerCase()) - throw new Error( - `transaction.from ${transaction.from.toLowerCase()} is different from the given address ${address.toLowerCase()}.` - ) - - await transaction.fillTransaction() - const hash = hasher(transaction) - const role = determineRoleToSign(transaction) - - const keyring = this.getKeyring(address) - if (keyring === undefined) throw new Error(`Failed to find the keyring from the wallet with the given address: ${address}`) - const sigs = keyring.signWithKeys(hash, transaction.chainId, role) - - transaction.appendSignatures(sigs) - - return transaction - } - - /** - * signs the transaction as a fee payer using one key and return the transactionHash - * - * @param {string} address An address of keyring in keyringContainer. - * @param {Transaction} transaction A transaction object. This should be `FEE_DELEGATED` type. - * @param {number} [index] An index of key to use for signing. - * @param {function} [hasher] A function to return hash of transaction. In order to use a custom hasher, the index must be defined. - * @return {string} - */ - async signFeePayerWithKey(address, transaction, index = 0, hasher = TransactionHasher.getHashForFeePayerSignature) { - // User parameter input cases - // (address transaction) / (address transaction index) / (address transaction index hasher) - if (_.isFunction(index)) throw new Error(`In order to pass a custom hasher, use the third parameter.`) - - if (!transaction.feePayer || transaction.feePayer === '0x') transaction.feePayer = address - - await transaction.fillTransaction() - const hash = hasher(transaction) - - const keyring = this.getKeyring(address) - if (keyring === undefined) throw new Error(`Failed to find keyring from wallet with ${address}`) - const sig = keyring.signWithKey(hash, transaction.chainId, KEY_ROLE.RoleFeePayerKey, index) - - transaction.appendFeePayerSignatures(sig) - - return hash - } - - /** - * signs the transaction as a fee payer using keys and return the transactionHash - * - * @param {string} address An address of keyring in keyringContainer. - * @param {Transaction} transaction A transaction object. This should be `FEE_DELEGATED` type. - * @param {function} [hasher] A function to return hash of transaction. - * @return {string} - */ - async signFeePayerWithKeys(address, transaction, hasher = TransactionHasher.getHashForFeePayerSignature) { - if (!transaction.feePayer || transaction.feePayer === '0x') transaction.feePayer = address - - await transaction.fillTransaction() - const hash = hasher(transaction) - - const keyring = this.getKeyring(address) - if (keyring === undefined) throw new Error(`Failed to find keyring from wallet with ${address}`) - const sigs = keyring.signWithKeys(hash, transaction.chainId, KEY_ROLE.RoleFeePayerKey) - - transaction.appendFeePayerSignatures(sigs) - - return hash - } -} - -function determineRoleToSign(tx) { - return tx.type.includes('ACCOUNT_UPDATE') ? KEY_ROLE.RoleAccountUpdateKey : KEY_ROLE.RoleTransactionKey -} - -module.exports = KeyringContainer diff --git a/test/packages/caver.wallet.js b/test/packages/caver.wallet.js index 7dded73d..f2d2ac93 100644 --- a/test/packages/caver.wallet.js +++ b/test/packages/caver.wallet.js @@ -66,28 +66,10 @@ function validateKeyringInWallet(data, { expectedAddress, expectedKey } = {}) { } } -describe('caver.wallet.generatePrivateKey', () => { - context('CAVERJS-UNIT-KEYRINGCONTAINER-049: input: no parameter', () => { - it('should return valid private key string', () => { - const result = caver.wallet.generatePrivateKey() - expect(utils.isValidPrivateKey(result)).to.be.true - }) - }) - - context('CAVERJS-UNIT-KEYRINGCONTAINER-050: input: entropy', () => { - it('should return valid private key string', () => { - const entropy = caver.utils.randomHex(32) - - const result = caver.wallet.generatePrivateKey(entropy) - expect(utils.isValidPrivateKey(result)).to.be.true - }) - }) -}) - describe('wallet.generate', () => { context('CAVERJS-UNIT-KEYRINGCONTAINER-001: input: valid number of keyring to make', () => { it('should generate keyring instances and add to in-memory wallet', () => { - const addSpy = sinon.spy(caver.wallet.keyringContainer, 'add') + const addSpy = sinon.spy(caver.wallet, 'add') const addresses = caver.wallet.generate(10) @@ -103,7 +85,7 @@ describe('wallet.generate', () => { context('CAVERJS-UNIT-KEYRINGCONTAINER-002: input: valid number of keyring to make, entropy', () => { it('should generate keyring instances and add to in-memory wallet', () => { - const addSpy = sinon.spy(caver.wallet.keyringContainer, 'add') + const addSpy = sinon.spy(caver.wallet, 'add') const entropy = caver.utils.randomHex(32) const addresses = caver.wallet.generate(10, entropy) @@ -122,7 +104,7 @@ describe('wallet.generate', () => { describe('wallet.newKeyring', () => { context('CAVERJS-UNIT-KEYRINGCONTAINER-003: input: address, single private key string', () => { it('should create keyring instances with parameters and add to in-memory wallet', () => { - const addSpy = sinon.spy(caver.wallet.keyringContainer, 'add') + const addSpy = sinon.spy(caver.wallet, 'add') const keyring = caver.wallet.keyring.generate() const added = caver.wallet.newKeyring(keyring.address, keyring.keys[0][0].privateKey) @@ -134,13 +116,9 @@ describe('wallet.newKeyring', () => { context('CAVERJS-UNIT-KEYRINGCONTAINER-004: input: address, multiple private key strings', () => { it('should create keyring instances with parameters and add to in-memory wallet', () => { - const addSpy = sinon.spy(caver.wallet.keyringContainer, 'add') + const addSpy = sinon.spy(caver.wallet, 'add') const address = caver.wallet.keyring.generate().address - const multiplePrivateKeys = [ - caver.wallet.generatePrivateKey(), - caver.wallet.generatePrivateKey(), - caver.wallet.generatePrivateKey(), - ] + const multiplePrivateKeys = caver.wallet.keyring.generateMultipleKeys(3) const added = caver.wallet.newKeyring(address, multiplePrivateKeys) validateKeyringInWallet(added, { expectedAddress: address, expectedKey: multiplePrivateKeys }) @@ -151,13 +129,9 @@ describe('wallet.newKeyring', () => { context('CAVERJS-UNIT-KEYRINGCONTAINER-005: input: address, private keys by roles(without empty role)', () => { it('should create keyring instances with parameters and add to in-memory wallet', () => { - const addSpy = sinon.spy(caver.wallet.keyringContainer, 'add') + const addSpy = sinon.spy(caver.wallet, 'add') const address = caver.wallet.keyring.generate().address - const roleBasedPrivateKeys = [ - [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()], - [caver.wallet.generatePrivateKey()], - [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()], - ] + const roleBasedPrivateKeys = caver.wallet.keyring.generateRoleBasedKeys([3, 1, 2]) const added = caver.wallet.newKeyring(address, roleBasedPrivateKeys) validateKeyringInWallet(added, { expectedAddress: address, expectedKey: roleBasedPrivateKeys }) @@ -168,9 +142,9 @@ describe('wallet.newKeyring', () => { context('CAVERJS-UNIT-KEYRINGCONTAINER-006: input: address, private keys by roles(with empty role)', () => { it('should create keyring instances with parameters and add to in-memory wallet', () => { - const addSpy = sinon.spy(caver.wallet.keyringContainer, 'add') + const addSpy = sinon.spy(caver.wallet, 'add') const address = caver.wallet.keyring.generate().address - const roleBasedPrivateKeys = [[], [], [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()]] + const roleBasedPrivateKeys = caver.wallet.keyring.generateRoleBasedKeys([0, 0, 2]) const added = caver.wallet.newKeyring(address, roleBasedPrivateKeys) validateKeyringInWallet(added, { expectedAddress: address, expectedKey: roleBasedPrivateKeys }) @@ -185,7 +159,7 @@ describe('wallet.updateKeyring', () => { it('should update key of keyring', () => { const coupled = caver.wallet.keyring.generate() const copySpy = sinon.spy(coupled, 'copy') - const decoupled = caver.wallet.keyring.createWithSingleKey(coupled.address, caver.wallet.generatePrivateKey()) + const decoupled = caver.wallet.keyring.createWithSingleKey(coupled.address, caver.wallet.keyring.generateSingleKey()) caver.wallet.add(decoupled) const updated = caver.wallet.updateKeyring(coupled) @@ -202,7 +176,7 @@ describe('wallet.updateKeyring', () => { it('should update key of keyring', () => { const coupled = caver.wallet.keyring.generate() const copySpy = sinon.spy(coupled, 'copy') - const decoupled = caver.wallet.keyring.createWithSingleKey(coupled.address, caver.wallet.generatePrivateKey()) + const decoupled = caver.wallet.keyring.createWithSingleKey(coupled.address, caver.wallet.keyring.generateSingleKey()) caver.wallet.add(coupled) const updated = caver.wallet.updateKeyring(decoupled) @@ -619,7 +593,7 @@ describe('wallet.signMessage', () => { const data = 'Some data' - const getKeyringSpy = sinon.spy(caver.wallet.keyringContainer, 'getKeyring') + const getKeyringSpy = sinon.spy(caver.wallet, 'getKeyring') const signMessageSpy = sinon.spy(keyring, 'signMessage') const signed = caver.wallet.signMessage(keyring.address, data) diff --git a/test/packages/caver.wallet.keyring.js b/test/packages/caver.wallet.keyring.js index 933c46ba..5438fd86 100644 --- a/test/packages/caver.wallet.keyring.js +++ b/test/packages/caver.wallet.keyring.js @@ -170,6 +170,145 @@ describe('caver.wallet.keyring.generate', () => { }) }) +describe('caver.wallet.generateSingleKey', () => { + context('CAVERJS-UNIT-KEYRING-147: input: no parameter', () => { + it('should return valid private key string', () => { + const result = caver.wallet.keyring.generateSingleKey() + expect(utils.isValidPrivateKey(result)).to.be.true + }) + }) + + context('CAVERJS-UNIT-KEYRING-148: input: entropy', () => { + it('should return valid private key string', () => { + const entropy = caver.utils.randomHex(32) + + const result = caver.wallet.keyring.generateSingleKey(entropy) + expect(utils.isValidPrivateKey(result)).to.be.true + }) + }) +}) + +describe('caver.wallet.generateMultipleKeys', () => { + context('CAVERJS-UNIT-KEYRING-149: input: number of keys', () => { + it('should return valid an array of private key strings', () => { + const result = caver.wallet.keyring.generateMultipleKeys(3) + expect(result.length).to.be.equal(3) + for (const p of result) { + expect(utils.isValidPrivateKey(p)).to.be.true + } + }) + }) + + context('CAVERJS-UNIT-KEYRING-150: input: number of keys, entropy', () => { + it('should return valid an array of private key strings', () => { + const entropy = caver.utils.randomHex(32) + + const result = caver.wallet.keyring.generateMultipleKeys(3, entropy) + expect(result.length).to.be.equal(3) + for (const p of result) { + expect(utils.isValidPrivateKey(p)).to.be.true + } + }) + }) + + context('CAVERJS-UNIT-KEYRING-151: input: no parameter', () => { + it('should return error', () => { + const expectedError = `To generate random multiple private keys, the number of keys should be defined.` + expect(() => caver.wallet.keyring.generateMultipleKeys()).to.throw(expectedError) + }) + }) + + context('CAVERJS-UNIT-KEYRING-152: input: entropy', () => { + it('should return error', () => { + const entropy = caver.utils.randomHex(32) + const expectedError = `To generate random multiple private keys, the number of keys should be defined.` + expect(() => caver.wallet.keyring.generateMultipleKeys(entropy)).to.throw(expectedError) + }) + }) +}) + +describe('caver.wallet.generateRoleBasedKeys', () => { + context('CAVERJS-UNIT-KEYRING-153: input: an array of the number of keys(less than role)', () => { + it('should return valid a role-based private key strings', () => { + const result = caver.wallet.keyring.generateRoleBasedKeys([3]) + + expect(result.length).to.be.equal(3) + expect(result[0].length).to.be.equal(3) + expect(result[1].length).to.be.equal(0) + expect(result[2].length).to.be.equal(0) + + for (const p of result[0]) { + expect(utils.isValidPrivateKey(p)).to.be.true + } + }) + }) + + context('CAVERJS-UNIT-KEYRING-154: input: an array of the number of keys', () => { + it('should return valid a role-based private key strings', () => { + const result = caver.wallet.keyring.generateRoleBasedKeys([3, 1, 2]) + + expect(result.length).to.be.equal(3) + expect(result[0].length).to.be.equal(3) + expect(result[1].length).to.be.equal(1) + expect(result[2].length).to.be.equal(2) + + for (let i = 0; i < result.length; i++) { + for (const p of result[i]) { + expect(utils.isValidPrivateKey(p)).to.be.true + } + } + }) + }) + + context('CAVERJS-UNIT-KEYRING-155: input: number of keys, entropy', () => { + it('should return valid a role-based private key strings', () => { + const entropy = caver.utils.randomHex(32) + + const result = caver.wallet.keyring.generateRoleBasedKeys([2, 2, 2], entropy) + + expect(result.length).to.be.equal(3) + expect(result[0].length).to.be.equal(2) + expect(result[1].length).to.be.equal(2) + expect(result[2].length).to.be.equal(2) + + for (let i = 0; i < result.length; i++) { + for (const p of result[i]) { + expect(utils.isValidPrivateKey(p)).to.be.true + } + } + }) + }) + + context('CAVERJS-UNIT-KEYRING-156: input: no parameter', () => { + it('should return error', () => { + const expectedError = `To generate random role-based private keys, an array containing the number of keys for each role should be defined.` + expect(() => caver.wallet.keyring.generateRoleBasedKeys()).to.throw(expectedError) + }) + }) + + context('CAVERJS-UNIT-KEYRING-157: input: entropy', () => { + it('should return error', () => { + const entropy = caver.utils.randomHex(32) + const expectedError = `To generate random role-based private keys, an array containing the number of keys for each role should be defined.` + expect(() => caver.wallet.keyring.generateRoleBasedKeys(entropy)).to.throw(expectedError) + }) + }) + + context('CAVERJS-UNIT-KEYRING-158: input: not array', () => { + it('should return error', () => { + const expectedError = `To generate random role-based private keys, an array containing the number of keys for each role should be defined.` + expect(() => caver.wallet.keyring.generateRoleBasedKeys(3)).to.throw(expectedError) + }) + }) + + context('CAVERJS-UNIT-KEYRING-159: input: too long array', () => { + it('should return error', () => { + const expectedError = `Unsupported role. The length of array should be less than ${caver.wallet.keyring.role.RoleLast}.` + expect(() => caver.wallet.keyring.generateRoleBasedKeys([1, 1, 1, 1, 1])).to.throw(expectedError) + }) + }) +}) + describe('caver.wallet.keyring.createFromPrivateKey', () => { context('CAVERJS-UNIT-KEYRING-005: input: single private key', () => { it('should create Keyring instance from private key string', () => { @@ -255,11 +394,7 @@ describe('caver.wallet.keyring.create', () => { context('CAVERJS-UNIT-KEYRING-013: input: address, multiple private key strings', () => { it('should create keyring instances with parameters and add to in-memory wallet', () => { const address = caver.wallet.keyring.generate().address - const multiplePrivateKeys = [ - caver.wallet.generatePrivateKey(), - caver.wallet.generatePrivateKey(), - caver.wallet.generatePrivateKey(), - ] + const multiplePrivateKeys = caver.wallet.keyring.generateMultipleKeys(3) const created = caver.wallet.keyring.create(address, multiplePrivateKeys) validateKeyring(created, { expectedAddress: address, expectedKey: multiplePrivateKeys }) }) @@ -268,11 +403,7 @@ describe('caver.wallet.keyring.create', () => { context('CAVERJS-UNIT-KEYRING-014: input: address, private keys by roles(without empty role)', () => { it('should create keyring instances with parameters and add to in-memory wallet', () => { const address = caver.wallet.keyring.generate().address - const roleBasedPrivateKeys = [ - [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()], - [caver.wallet.generatePrivateKey()], - [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()], - ] + const roleBasedPrivateKeys = caver.wallet.keyring.generateRoleBasedKeys([3, 1, 2]) const created = caver.wallet.keyring.create(address, roleBasedPrivateKeys) validateKeyring(created, { expectedAddress: address, expectedKey: roleBasedPrivateKeys }) }) @@ -281,7 +412,7 @@ describe('caver.wallet.keyring.create', () => { context('CAVERJS-UNIT-KEYRING-015: input: address, private keys by roles(with empty role)', () => { it('should create keyring instances with parameters and add to in-memory wallet', () => { const address = caver.wallet.keyring.generate().address - const roleBasedPrivateKeys = [[], [], [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()]] + const roleBasedPrivateKeys = caver.wallet.keyring.generateRoleBasedKeys([0, 0, 2]) const created = caver.wallet.keyring.create(address, roleBasedPrivateKeys) validateKeyring(created, { expectedAddress: address, expectedKey: roleBasedPrivateKeys }) }) @@ -291,9 +422,9 @@ describe('caver.wallet.keyring.create', () => { it('should throw error if key is invalid format', () => { const address = caver.wallet.keyring.generate().address const invalidKey = [ - caver.wallet.generatePrivateKey(), + caver.wallet.keyring.generateSingleKey(), [], - [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()], + [caver.wallet.keyring.generateSingleKey(), caver.wallet.keyring.generateSingleKey()], ] const expectedError = `Unsupported key type: ${typeof invalidKey}` @@ -339,7 +470,7 @@ describe('caver.wallet.keyring.createWithSingleKey', () => { context('CAVERJS-UNIT-KEYRING-020: input: valid address, multiple private key array', () => { it('should throw error', () => { const keyring = caver.wallet.keyring.generate() - const arr = [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()] + const arr = caver.wallet.keyring.generateMultipleKeys(2) const errormsg = `Invalid format of parameter. Use 'fromMultipleKey' or 'fromRoleBasedKey' for two or more keys.` @@ -355,7 +486,7 @@ describe('caver.wallet.keyring.createWithMultipleKey', () => { context('CAVERJS-UNIT-KEYRING-021: input: valid address, valid private key array', () => { it('should create Keyring instance', () => { const keyring = caver.wallet.keyring.generate() - const arr = [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()] + const arr = caver.wallet.keyring.generateMultipleKeys(2) const result = caver.wallet.keyring.createWithMultipleKey(keyring.address, arr) validateKeyring(result, { expectedKey: arr, expectedAddress: keyring.address }) @@ -376,13 +507,13 @@ describe('caver.wallet.keyring.createWithMultipleKey', () => { context('CAVERJS-UNIT-KEYRING-023: input: valid address, invalid format of private key array', () => { it('should throw error', () => { const keyring = caver.wallet.keyring.generate() - let arr = [[caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()]] + let arr = caver.wallet.keyring.generateRoleBasedKeys([2]) const errormsg = `Invalid format of parameter. 'keyArray' should be an array of private key strings.` expect(() => caver.wallet.keyring.createWithMultipleKey(keyring.address, arr)).to.throws(errormsg) - arr = [caver.wallet.generatePrivateKey(), [caver.wallet.generatePrivateKey()]] + arr = [caver.wallet.keyring.generateSingleKey(), [caver.wallet.keyring.generateSingleKey()]] expect(() => caver.wallet.keyring.createWithMultipleKey(keyring.address, arr)).to.throws(errormsg) expect(() => caver.wallet.keyring.createWithMultipleKey(keyring.address, undefined)).to.throws(errormsg) @@ -395,11 +526,7 @@ describe('caver.wallet.keyring.createWithRoleBasedKey', () => { context('CAVERJS-UNIT-KEYRING-024: input: valid address, valid role based private key array', () => { it('should create Keyring instance', () => { const keyring = caver.wallet.keyring.generate() - const arr = [ - [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()], - [caver.wallet.generatePrivateKey()], - [caver.wallet.generatePrivateKey()], - ] + const arr = caver.wallet.keyring.generateRoleBasedKeys([2, 1, 1]) const result = caver.wallet.keyring.createWithRoleBasedKey(keyring.address, arr) validateKeyring(result, { expectedKey: arr, expectedAddress: keyring.address }) @@ -410,9 +537,9 @@ describe('caver.wallet.keyring.createWithRoleBasedKey', () => { it('should throw error if the role-based key does not define the key to be used for the role in the form of an array.', () => { const keyring = caver.wallet.keyring.generate() const arr = [ - [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()], - caver.wallet.generatePrivateKey(), - caver.wallet.generatePrivateKey(), + [caver.wallet.keyring.generateSingleKey(), caver.wallet.keyring.generateSingleKey()], + caver.wallet.keyring.generateSingleKey(), + caver.wallet.keyring.generateSingleKey(), ] const expectedError = `Invalid format of parameter. 'roledBasedKeyArray' should be in the form of an array defined as an array for the keys to be used for each role.` @@ -434,12 +561,12 @@ describe('caver.wallet.keyring.createWithRoleBasedKey', () => { context('CAVERJS-UNIT-KEYRING-027: input: valid address, invalid format of role based private key array (1 dimensional array)', () => { it('should throw error', () => { const keyring = caver.wallet.keyring.generate() - const arr = [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()] + const arr = caver.wallet.keyring.generateMultipleKeys(2) const errormsg = `Invalid format of parameter. 'roledBasedKeyArray' should be in the form of an array defined as an array for the keys to be used for each role.` expect(() => caver.wallet.keyring.createWithRoleBasedKey(keyring.address, arr)).to.throws(errormsg) - expect(() => caver.wallet.keyring.createWithRoleBasedKey(keyring.address, caver.wallet.generatePrivateKey())).to.throws( + expect(() => caver.wallet.keyring.createWithRoleBasedKey(keyring.address, caver.wallet.keyring.generateSingleKey())).to.throws( errormsg ) }) @@ -450,7 +577,7 @@ describe('caver.wallet.keyring.encrypt', () => { context('CAVERJS-UNIT-KEYRING-028: input: private key string, password', () => { it('should encrypted as v4Keystore', () => { const password = 'password' - const privateKey = caver.wallet.generatePrivateKey() + const privateKey = caver.wallet.keyring.generateSingleKey() const keyring = caver.wallet.keyring.createFromPrivateKey(privateKey) const result = caver.wallet.keyring.encrypt(privateKey, password) @@ -507,7 +634,7 @@ describe('caver.wallet.keyring.encrypt', () => { context('CAVERJS-UNIT-KEYRING-031: input: multiple private key, password, {address}', () => { it('should encrypted as v4Keystore', () => { const password = 'password' - const privateKeys = [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()] + const privateKeys = caver.wallet.keyring.generateMultipleKeys(3) const address = caver.wallet.keyring.generate().address const result = caver.wallet.keyring.encrypt(privateKeys, password, { address }) @@ -518,7 +645,7 @@ describe('caver.wallet.keyring.encrypt', () => { context('CAVERJS-UNIT-KEYRING-032: input: multiple private key, password', () => { it('should throw error', () => { const password = 'password' - const privateKeys = [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()] + const privateKeys = caver.wallet.keyring.generateMultipleKeys(3) const errormsg = `The address must be defined inside the options object to encrypt multiple keys.` @@ -529,16 +656,7 @@ describe('caver.wallet.keyring.encrypt', () => { context('CAVERJS-UNIT-KEYRING-033: input: role based private key, password, {address}', () => { it('should encrypted as v4Keystore', () => { const password = 'password' - const privateKeys = [ - [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()], - [caver.wallet.generatePrivateKey()], - [ - caver.wallet.generatePrivateKey(), - caver.wallet.generatePrivateKey(), - caver.wallet.generatePrivateKey(), - caver.wallet.generatePrivateKey(), - ], - ] + const privateKeys = caver.wallet.keyring.generateRoleBasedKeys([2, 1, 4]) const address = caver.wallet.keyring.generate().address const result = caver.wallet.keyring.encrypt(privateKeys, password, { address }) @@ -549,11 +667,7 @@ describe('caver.wallet.keyring.encrypt', () => { context('CAVERJS-UNIT-KEYRING-034: input: role based private key(with empty role), password, {address}', () => { it('should encrypted as v4Keystore', () => { const password = 'password' - const privateKeys = [ - [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()], - [], - [caver.wallet.generatePrivateKey()], - ] + const privateKeys = caver.wallet.keyring.generateRoleBasedKeys([2, 0, 1]) const address = caver.wallet.keyring.generate().address const result = caver.wallet.keyring.encrypt(privateKeys, password, { address }) @@ -564,11 +678,7 @@ describe('caver.wallet.keyring.encrypt', () => { context('CAVERJS-UNIT-KEYRING-035: input: role based private key, password', () => { it('should throw error', () => { const password = 'password' - const privateKeys = [ - [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()], - [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()], - [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()], - ] + const privateKeys = caver.wallet.keyring.generateRoleBasedKeys([2, 2, 2]) const errormsg = `The address must be defined inside the options object to encrypt multiple keys.` @@ -717,7 +827,7 @@ describe('caver.wallet.keyring.encryptV3', () => { context('CAVERJS-UNIT-KEYRING-038: input: private key string, password', () => { it('should encrypted as v3Keystore', () => { const password = 'password' - const privateKey = caver.wallet.generatePrivateKey() + const privateKey = caver.wallet.keyring.generateSingleKey() const keyring = caver.wallet.keyring.createFromPrivateKey(privateKey) const result = caver.wallet.keyring.encryptV3(privateKey, password) @@ -773,7 +883,7 @@ describe('caver.wallet.keyring.encryptV3', () => { context('CAVERJS-UNIT-KEYRING-041: input: multiple private key, password, {address}', () => { it('should throw error', () => { const password = 'password' - const privateKeys = [caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey(), caver.wallet.generatePrivateKey()] + const privateKeys = caver.wallet.keyring.generateMultipleKeys(3) const address = caver.wallet.keyring.generate().address const errormsg = `Invalid parameter. key should be a private key string, KlaytnWalletKey or instance of Keyring` @@ -797,7 +907,7 @@ describe('caver.wallet.keyring.decrypt', () => { context('CAVERJS-UNIT-KEYRING-043: coupled keyring', () => { it('should return valid keyring', () => { const password = 'password' - const privateKey = caver.wallet.generatePrivateKey() + const privateKey = caver.wallet.keyring.generateSingleKey() const keyring = caver.wallet.keyring.createFromPrivateKey(privateKey) const encrypted = caver.wallet.keyring.encrypt(privateKey, password) diff --git a/test/packages/utils.js b/test/packages/utils.js index b6874213..be56aaf1 100644 --- a/test/packages/utils.js +++ b/test/packages/utils.js @@ -24,9 +24,6 @@ const utils = require('../../packages/caver-utils') const Keyring = require('../../packages/caver-wallet/src/keyring/keyring') const { KEY_ROLE } = require('../../packages/caver-wallet/src/keyring/keyringHelper') -const Wallet = require('../../packages/caver-wallet') - -const wallet = new Wallet() const unitMap = { peb: '1', @@ -44,17 +41,13 @@ const unitMap = { const generateDecoupledKeyring = () => { const keyring = Keyring.generate() - keyring.keys = wallet.generatePrivateKey() + keyring.keys = Keyring.generateSingleKey() return keyring } const generateMultiSigKeyring = (num = 3) => { const keyring = Keyring.generate() - const multipleKeys = [] - for (let i = 0; i < num; i++) { - multipleKeys.push(wallet.generatePrivateKey()) - } - keyring.keys = multipleKeys + keyring.keys = Keyring.generateMultipleKeys(num) return keyring } @@ -63,15 +56,7 @@ const generateRoleBasedKeyring = numArr => { numArr = Array(KEY_ROLE.RoleLast).fill(1) } const keyring = Keyring.generate() - const roleBased = [] - for (let i = 0; i < numArr.length; i++) { - const keys = [] - for (let j = 0; j < numArr[i]; j++) { - keys.push(wallet.generatePrivateKey()) - } - roleBased.push(keys) - } - keyring.keys = roleBased + keyring.keys = Keyring.generateRoleBasedKeys(numArr) return keyring } From 7634f00966ff8c1c424ce92383b5bca81ee4e4c8 Mon Sep 17 00:00:00 2001 From: Jasmine <32922423+jimni1222@users.noreply.github.com> Date: Tue, 26 May 2020 14:21:20 +0900 Subject: [PATCH 4/5] Apply suggestions from code review Co-authored-by: kale --- packages/caver-wallet/src/keyring/keyring.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/caver-wallet/src/keyring/keyring.js b/packages/caver-wallet/src/keyring/keyring.js index 750ce0ca..20400880 100644 --- a/packages/caver-wallet/src/keyring/keyring.js +++ b/packages/caver-wallet/src/keyring/keyring.js @@ -68,7 +68,7 @@ class Keyring { * * @param {number} num A length of keys. * @param {string} entropy A random string to increase entropy. - * @return {Keyring} + * @return {Array.} */ static generateMultipleKeys(num, entropy) { if (num === undefined || !_.isNumber(num) || _.isString(num)) { @@ -89,7 +89,7 @@ class Keyring { * * @param {Array.} numArr An array containing the number of keys for each role. * @param {string} entropy A random string to increase entropy. - * @return {Keyring} + * @return {Array.>} */ static generateRoleBasedKeys(numArr, entropy) { if (numArr === undefined || !_.isArray(numArr) || _.isString(numArr)) { From ed535ae6421f78804bfc80c40bddeb26120c0c62 Mon Sep 17 00:00:00 2001 From: Jasmine/kimjimin Date: Tue, 26 May 2020 14:28:04 +0900 Subject: [PATCH 5/5] KeyringContainer use sign function in Transaction --- packages/caver-wallet/src/index.js | 71 +++------------ test/packages/caver.wallet.js | 135 +++++++++++++---------------- 2 files changed, 72 insertions(+), 134 deletions(-) diff --git a/packages/caver-wallet/src/index.js b/packages/caver-wallet/src/index.js index fa7042d0..47535071 100644 --- a/packages/caver-wallet/src/index.js +++ b/packages/caver-wallet/src/index.js @@ -19,7 +19,6 @@ const _ = require('lodash') const Keyring = require('./keyring/keyring') const TransactionHasher = require('../../caver-transaction/src/transactionHasher/transactionHasher') -const { KEY_ROLE } = require('./keyring/keyringHelper') const utils = require('../../caver-utils/src') /** @@ -193,27 +192,11 @@ class KeyringContainer { * @return {Transaction} */ async signWithKey(address, transaction, index = 0, hasher = TransactionHasher.getHashForSignature) { - if (!transaction.from || transaction.from === '0x') transaction.from = address - if (transaction.from.toLowerCase() !== address.toLowerCase()) - throw new Error( - `transaction.from ${transaction.from.toLowerCase()} is different from the given address ${address.toLowerCase()}.` - ) - - // User parameter input cases - // (address transaction) / (address transaction index) / (address transaction index hasher) - if (_.isFunction(index)) throw new Error(`In order to pass a custom hasher, use the third parameter.`) - - await transaction.fillTransaction() - const hash = hasher(transaction) - const role = determineRoleToSign(transaction) - const keyring = this.getKeyring(address) if (keyring === undefined) throw new Error(`Failed to find keyring from wallet with ${address}`) - const sig = keyring.signWithKey(hash, transaction.chainId, role, index) - - transaction.appendSignatures(sig) + const signed = await transaction.signWithKey(keyring, index, hasher) - return transaction + return signed } /** @@ -225,23 +208,11 @@ class KeyringContainer { * @return {Transaction} */ async signWithKeys(address, transaction, hasher = TransactionHasher.getHashForSignature) { - if (!transaction.from || transaction.from === '0x') transaction.from = address - if (transaction.from.toLowerCase() !== address.toLowerCase()) - throw new Error( - `transaction.from ${transaction.from.toLowerCase()} is different from the given address ${address.toLowerCase()}.` - ) - - await transaction.fillTransaction() - const hash = hasher(transaction) - const role = determineRoleToSign(transaction) - const keyring = this.getKeyring(address) if (keyring === undefined) throw new Error(`Failed to find the keyring from the wallet with the given address: ${address}`) - const sigs = keyring.signWithKeys(hash, transaction.chainId, role) - - transaction.appendSignatures(sigs) + const signed = await transaction.signWithKeys(keyring, hasher) - return transaction + return signed } /** @@ -251,25 +222,14 @@ class KeyringContainer { * @param {Transaction} transaction A transaction object. This should be `FEE_DELEGATED` type. * @param {number} [index] An index of key to use for signing. * @param {function} [hasher] A function to return hash of transaction. In order to use a custom hasher, the index must be defined. - * @return {string} + * @return {Transaction} */ async signFeePayerWithKey(address, transaction, index = 0, hasher = TransactionHasher.getHashForFeePayerSignature) { - // User parameter input cases - // (address transaction) / (address transaction index) / (address transaction index hasher) - if (_.isFunction(index)) throw new Error(`In order to pass a custom hasher, use the third parameter.`) - - if (!transaction.feePayer || transaction.feePayer === '0x') transaction.feePayer = address - - await transaction.fillTransaction() - const hash = hasher(transaction) - const keyring = this.getKeyring(address) if (keyring === undefined) throw new Error(`Failed to find keyring from wallet with ${address}`) - const sig = keyring.signWithKey(hash, transaction.chainId, KEY_ROLE.RoleFeePayerKey, index) - - transaction.appendFeePayerSignatures(sig) + const signed = await transaction.signFeePayerWithKey(keyring, index, hasher) - return hash + return signed } /** @@ -278,26 +238,15 @@ class KeyringContainer { * @param {string} address An address of keyring in keyringContainer. * @param {Transaction} transaction A transaction object. This should be `FEE_DELEGATED` type. * @param {function} [hasher] A function to return hash of transaction. - * @return {string} + * @return {Transaction} */ async signFeePayerWithKeys(address, transaction, hasher = TransactionHasher.getHashForFeePayerSignature) { - if (!transaction.feePayer || transaction.feePayer === '0x') transaction.feePayer = address - - await transaction.fillTransaction() - const hash = hasher(transaction) - const keyring = this.getKeyring(address) if (keyring === undefined) throw new Error(`Failed to find keyring from wallet with ${address}`) - const sigs = keyring.signWithKeys(hash, transaction.chainId, KEY_ROLE.RoleFeePayerKey) - - transaction.appendFeePayerSignatures(sigs) + const signed = await transaction.signFeePayerWithKeys(keyring, hasher) - return hash + return signed } } -function determineRoleToSign(tx) { - return tx.type.includes('ACCOUNT_UPDATE') ? KEY_ROLE.RoleAccountUpdateKey : KEY_ROLE.RoleTransactionKey -} - module.exports = KeyringContainer diff --git a/test/packages/caver.wallet.js b/test/packages/caver.wallet.js index f2d2ac93..d849d6b9 100644 --- a/test/packages/caver.wallet.js +++ b/test/packages/caver.wallet.js @@ -30,10 +30,13 @@ const expect = chai.expect const testRPCURL = require('../testrpc') const Caver = require('../../index.js') -const utils = require('../../packages/caver-utils') const Keyring = require('../../packages/caver-wallet/src/keyring/keyring') const PrivateKey = require('../../packages/caver-wallet/src/keyring/privateKey') +const ValueTransfer = require('../../packages/caver-transaction/src/transactionTypes/valueTransfer/valueTransfer') +const FeeDelegatedValueTransfer = require('../../packages/caver-transaction/src/transactionTypes/valueTransfer/feeDelegatedValueTransfer') +const AccountUpdate = require('../../packages/caver-transaction/src/transactionTypes/accountUpdate/accountUpdate') + const { generateDecoupledKeyring, generateMultiSigKeyring, generateRoleBasedKeyring } = require('./utils') let caver @@ -377,7 +380,7 @@ describe('wallet.signWithKey', () => { it('should sign to transaction and return hash', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockValueTransfer(keyring) + const vt = generateValueTransfer(keyring) const fillFormatSpy = sinon.spy(vt, 'fillTransaction') const signSpy = sinon.spy(keyring, 'signWithKey') @@ -395,7 +398,7 @@ describe('wallet.signWithKey', () => { it('should sign to transaction and return hash', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockValueTransfer(keyring) + const vt = generateValueTransfer(keyring) const fillFormatSpy = sinon.spy(vt, 'fillTransaction') const signSpy = sinon.spy(keyring, 'signWithKey') @@ -413,7 +416,7 @@ describe('wallet.signWithKey', () => { it('should sign to transaction and return hash', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockValueTransfer(keyring) + const vt = generateValueTransfer(keyring) const txHash = '0xd4aab6590bdb708d1d3eafe95a967dafcd2d7cde197e512f3f0b8158e7b65fd1' @@ -426,7 +429,7 @@ describe('wallet.signWithKey', () => { it('should sign to transaction and return hash', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockValueTransfer(keyring) + const vt = generateValueTransfer(keyring) const txHash = '0xd4aab6590bdb708d1d3eafe95a967dafcd2d7cde197e512f3f0b8158e7b65fd1' @@ -446,7 +449,7 @@ describe('wallet.signWithKey', () => { it('should sign to transaction and return hash', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const updateTx = new mockAccountUpdate(keyring) + const updateTx = generateAccountUpdate(keyring) const txHash = '0xd4aab6590bdb708d1d3eafe95a967dafcd2d7cde197e512f3f0b8158e7b65fd1' @@ -466,7 +469,7 @@ describe('wallet.signWithKey', () => { it('should throw error when index is invalid', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockValueTransfer(keyring) + const vt = generateValueTransfer(keyring) const invalidIndex = 3 const expectedError = `Invalid index(${invalidIndex}): index must be less than the length of keys(${invalidIndex}).` @@ -480,7 +483,7 @@ describe('wallet.signWithKey', () => { it('should throw error when transaction hash is invalid', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockValueTransfer(keyring) + const vt = generateValueTransfer(keyring) const invalidTxHash = 'invalidTxHash' @@ -494,7 +497,7 @@ describe('wallet.signWithKey', () => { it('should throw error when keyring is not existed in wallet', async () => { const keyring = generateRoleBasedKeyring([3, 2, 4]) - const vt = new mockValueTransfer(keyring) + const vt = generateValueTransfer(keyring) const expectedError = `Failed to find keyring from wallet with ${keyring.address}` await expect(caver.wallet.signWithKey(keyring.address, vt)).to.be.rejectedWith(expectedError) @@ -507,7 +510,7 @@ describe('wallet.signWithKeys', () => { it('should sign to transaction and return hash', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockValueTransfer(keyring) + const vt = generateValueTransfer(keyring) const fillFormatSpy = sinon.spy(vt, 'fillTransaction') const signSpy = sinon.spy(keyring, 'signWithKeys') @@ -525,7 +528,7 @@ describe('wallet.signWithKeys', () => { it('should sign to transaction and return hash', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockValueTransfer(keyring) + const vt = generateValueTransfer(keyring) const txHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550' @@ -545,7 +548,7 @@ describe('wallet.signWithKeys', () => { it('should sign to transaction and return hash', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const updateTx = new mockAccountUpdate(keyring) + const updateTx = generateAccountUpdate(keyring) const txHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550' @@ -565,7 +568,7 @@ describe('wallet.signWithKeys', () => { it('should throw error when transaction hash is invalid', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockValueTransfer(keyring) + const vt = generateValueTransfer(keyring) const invalidTxHash = 'invalidTxHash' @@ -578,7 +581,7 @@ describe('wallet.signWithKeys', () => { it('should throw error when keyring is not existed in wallet', async () => { const keyring = generateRoleBasedKeyring([3, 2, 4]) - const vt = new mockValueTransfer(keyring) + const vt = generateValueTransfer(keyring) const expectedError = `Failed to find the keyring from the wallet with the given address: ${keyring.address}` await expect(caver.wallet.signWithKeys(keyring.address, vt)).to.be.rejectedWith(expectedError) @@ -625,15 +628,14 @@ describe('wallet.signFeePayerWithKey', () => { it('should sign to transaction with roleFeePayerKey and return hash', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockFeeDelegatedValueTransfer(keyring) + const vt = generateFeeDelegatedValueTransfer(keyring) const fillFormatSpy = sinon.spy(vt, 'fillTransaction') const signSpy = sinon.spy(keyring, 'signWithKey') const appendSpy = sinon.spy(vt, 'appendFeePayerSignatures') - const hash = await caver.wallet.signFeePayerWithKey(keyring.address, vt) + await caver.wallet.signFeePayerWithKey(keyring.address, vt) - expect(utils.isTxHashStrict(hash)).to.be.true expect(fillFormatSpy).to.have.been.calledOnce expect(signSpy).to.have.been.calledOnce expect(appendSpy).to.have.been.calledOnce @@ -644,15 +646,14 @@ describe('wallet.signFeePayerWithKey', () => { it('should sign to transaction with roleFeePayerKey and return hash', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockFeeDelegatedValueTransfer(keyring) + const vt = generateFeeDelegatedValueTransfer(keyring) const fillFormatSpy = sinon.spy(vt, 'fillTransaction') const signSpy = sinon.spy(keyring, 'signWithKey') const appendSpy = sinon.spy(vt, 'appendFeePayerSignatures') - const hash = await caver.wallet.signFeePayerWithKey(keyring.address, vt, 2) + await caver.wallet.signFeePayerWithKey(keyring.address, vt, 2) - expect(utils.isTxHashStrict(hash)).to.be.true expect(fillFormatSpy).to.have.been.calledOnce expect(signSpy).to.have.been.calledOnce expect(appendSpy).to.have.been.calledOnce @@ -663,7 +664,7 @@ describe('wallet.signFeePayerWithKey', () => { it('should sign to transaction with roleFeePayerKey and return hash', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockFeeDelegatedValueTransfer(keyring) + const vt = generateFeeDelegatedValueTransfer(keyring) const txHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550' @@ -676,7 +677,7 @@ describe('wallet.signFeePayerWithKey', () => { it('should sign to transaction with roleFeePayerKey and return hash', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockFeeDelegatedValueTransfer(keyring) + const vt = generateFeeDelegatedValueTransfer(keyring) const txHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550' @@ -684,9 +685,8 @@ describe('wallet.signFeePayerWithKey', () => { const signSpy = sinon.spy(keyring, 'signWithKey') const appendSpy = sinon.spy(vt, 'appendFeePayerSignatures') - const hash = await caver.wallet.signFeePayerWithKey(keyring.address, vt, 1, () => txHash) + await caver.wallet.signFeePayerWithKey(keyring.address, vt, 1, () => txHash) - expect(utils.isTxHashStrict(hash)).to.be.true expect(fillFormatSpy).to.have.been.calledOnce expect(signSpy).to.have.been.calledWith(txHash, '0x7e3', 2, 1) expect(appendSpy).to.have.been.calledOnce @@ -697,7 +697,7 @@ describe('wallet.signFeePayerWithKey', () => { it('should throw error when index is invalid', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockFeeDelegatedValueTransfer(keyring) + const vt = generateFeeDelegatedValueTransfer(keyring) const invalidIndex = 4 const expectedError = `Invalid index(${invalidIndex}): index must be less than the length of keys(${invalidIndex}).` @@ -711,7 +711,7 @@ describe('wallet.signFeePayerWithKey', () => { it('should throw error when transaction hash is invalid', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockFeeDelegatedValueTransfer(keyring) + const vt = generateFeeDelegatedValueTransfer(keyring) const invalidTxHash = 'invalidTxHash' @@ -727,7 +727,7 @@ describe('wallet.signFeePayerWithKey', () => { it('should throw error when keyring is not existed in wallet', async () => { const keyring = generateRoleBasedKeyring([3, 2, 4]) - const vt = new mockFeeDelegatedValueTransfer(keyring) + const vt = generateFeeDelegatedValueTransfer(keyring) const expectedError = `Failed to find keyring from wallet with ${keyring.address}` await expect(caver.wallet.signFeePayerWithKey(keyring.address, vt)).to.be.rejectedWith(expectedError) @@ -740,15 +740,14 @@ describe('wallet.signFeePayerWithKeys', () => { it('should sign to transaction with keys in roleFeePayerKey and return hash', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockFeeDelegatedValueTransfer(keyring) + const vt = generateFeeDelegatedValueTransfer(keyring) const fillFormatSpy = sinon.spy(vt, 'fillTransaction') const signSpy = sinon.spy(keyring, 'signWithKeys') const appendSpy = sinon.spy(vt, 'appendFeePayerSignatures') - const hash = await caver.wallet.signFeePayerWithKeys(keyring.address, vt) + await caver.wallet.signFeePayerWithKeys(keyring.address, vt) - expect(utils.isTxHashStrict(hash)).to.be.true expect(fillFormatSpy).to.have.been.calledOnce expect(signSpy).to.have.been.calledOnce expect(appendSpy).to.have.been.calledOnce @@ -759,7 +758,7 @@ describe('wallet.signFeePayerWithKeys', () => { it('should sign to transaction with keys in roleFeePayerKey and return hash', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockFeeDelegatedValueTransfer(keyring) + const vt = generateFeeDelegatedValueTransfer(keyring) const txHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550' @@ -767,9 +766,8 @@ describe('wallet.signFeePayerWithKeys', () => { const signSpy = sinon.spy(keyring, 'signWithKeys') const appendSpy = sinon.spy(vt, 'appendFeePayerSignatures') - const hash = await caver.wallet.signFeePayerWithKeys(keyring.address, vt, () => txHash) + await caver.wallet.signFeePayerWithKeys(keyring.address, vt, () => txHash) - expect(utils.isTxHashStrict(hash)).to.be.true expect(fillFormatSpy).to.have.been.calledOnce expect(signSpy).to.have.been.calledWith(txHash, '0x7e3', 2) expect(appendSpy).to.have.been.calledOnce @@ -780,7 +778,7 @@ describe('wallet.signFeePayerWithKeys', () => { it('should throw error when transaction hash is invalid', async () => { const keyring = caver.wallet.add(generateRoleBasedKeyring([3, 2, 4])) - const vt = new mockFeeDelegatedValueTransfer(keyring) + const vt = generateFeeDelegatedValueTransfer(keyring) const invalidTxHash = 'invalidTxHash' @@ -793,7 +791,7 @@ describe('wallet.signFeePayerWithKeys', () => { it('should throw error when keyring is not existed in wallet', async () => { const keyring = generateRoleBasedKeyring([3, 2, 4]) - const vt = new mockFeeDelegatedValueTransfer(keyring) + const vt = generateFeeDelegatedValueTransfer(keyring) const expectedError = `Failed to find keyring from wallet with ${keyring.address}` await expect(caver.wallet.signFeePayerWithKeys(keyring.address, vt)).to.be.rejectedWith(expectedError) @@ -801,46 +799,37 @@ describe('wallet.signFeePayerWithKeys', () => { }) }) -class mockValueTransfer { - constructor(keyring) { - this.type = 'VALUE_TRANSFER' - this.from = keyring.address - this.to = keyring.address - this.value = '0x1' - this.gas = '0x15f90' - this.chainId = '0x7e3' - this.nonce = '0x0' - this.gasPrice = '0x5d21dba00' - this.signatures = [] - - this.getRLPEncodingForSignature = () => '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550' - this.fillTransaction = () => {} - this.appendSignatures = () => {} - } +function generateValueTransfer(keyring) { + return new ValueTransfer({ + from: keyring.address, + to: keyring.address, + value: '0x1', + gas: '0x15f90', + chainId: '0x7e3', + nonce: '0x0', + gasPrice: '0x5d21dba00', + }) } -class mockAccountUpdate { - constructor(keyring) { - this.type = 'ACCOUNT_UPDATE' - this.from = keyring.address - this.account = keyring.toAccount() - this.gas = '0x15f90' - this.chainId = '0x7e3' - this.nonce = '0x0' - this.gasPrice = '0x5d21dba00' - - this.getRLPEncodingForSignature = () => '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550' - this.fillTransaction = () => {} - this.appendSignatures = () => {} - } +function generateFeeDelegatedValueTransfer(keyring) { + return new FeeDelegatedValueTransfer({ + from: keyring.address, + to: keyring.address, + value: '0x1', + gas: '0x15f90', + chainId: '0x7e3', + nonce: '0x0', + gasPrice: '0x5d21dba00', + }) } -class mockFeeDelegatedValueTransfer extends mockValueTransfer { - constructor(keyring) { - super(keyring) - this.type = 'FEE_DELEGATED_VALUE_TRANSFER' - - this.getRLPEncodingForFeePayerSignature = () => '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550' - this.appendFeePayerSignatures = () => {} - } +function generateAccountUpdate(keyring) { + return new AccountUpdate({ + from: keyring.address, + account: keyring.toAccount(), + gas: '0x15f90', + chainId: '0x7e3', + nonce: '0x0', + gasPrice: '0x5d21dba00', + }) }