six-caver-js
Version:
caver-js is a JavaScript API library that allows developers to interact with a Klaytn node
258 lines (225 loc) • 8.54 kB
JavaScript
/*
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 <http://www.gnu.org/licenses/>.
*/
/* eslint-disable no-unused-vars */
/* eslint-disable class-methods-use-this */
const _ = require('lodash')
const utils = require('../../../caver-utils')
const PrivateKey = require('./privateKey')
const { KEY_ROLE } = require('./keyringHelper')
const Account = require('../../../caver-account')
const { fillWeightedMultiSigOptionsForMultiSig } = require('../../../caver-account/src/accountKey/accountKeyHelper')
const { validateForSigning, validateIndexWithKeys, encryptKey, formatEncrypted } = require('./keyringHelper')
/**
* representing a Keyring which includes `address` and `private keys`.
* @class
*/
class MultipleKeyring {
/**
* creates a MultipleKeyring.
* @param {string} address - The address of keyring.
* @param {Array.<string>|Array.<PrivateKey>} keys - The keys to use in MultipleKeyring.
*/
constructor(address, keys) {
this.address = address
this.keys = keys
}
/**
* @type {string}
*/
get address() {
return this._address
}
set address(addressInput) {
if (!utils.isAddress(addressInput)) throw new Error(`Invalid address : ${addressInput}`)
this._address = utils.addHexPrefix(addressInput).toLowerCase()
}
/**
* @type {Array.<PrivateKey>}
*/
get keys() {
return this._keys
}
set keys(keyInput) {
if (keyInput === null) {
this._key = null
return
}
this._keys = formattingForKeyInKeyring(keyInput)
}
/**
* returns public key strings.
*
* @param {boolean} [compressed] Whether in compressed format or not.
* @return {Array.<string>}
*/
getPublicKey(compressed = false) {
const publicKeys = []
for (let i = 0; i < this.keys.length; i++) {
publicKeys.push(this.keys[i].getPublicKey(compressed))
}
return publicKeys
}
/**
* returns a copied multipleKeyring instance
*
* @return {MultipleKeyring}
*/
copy() {
return new MultipleKeyring(this.address, this.keys)
}
/**
* signs with transactionHash with key and returns signature(s).
*
* @param {string} transactionHash The hash of transaction.
* @param {string|number} chainId The chainId specific to the network.
* @param {number} role A number indicating the role of the key. You can use `caver.wallet.keyring.role`.
* @param {number} [index] The index of the key to be used. If index is undefined, all private keys in keyring will be used.
* @return {Array.<string>|Array.<Array.<string>>}
*/
sign(transactionHash, chainId, role, index) {
validateForSigning(transactionHash, chainId)
const keys = this.getKeyByRole(role)
if (index !== undefined) {
validateIndexWithKeys(index, keys.length)
return keys[index].sign(transactionHash, chainId)
}
const signatures = []
for (const k of keys) {
signatures.push(k.sign(transactionHash, chainId))
}
return signatures
}
/**
* signs with hashed message and returns result object that includes `signatures`, `message` and `messageHash`
*
* @param {string} message The message string to sign.
* @param {number} role A number indicating the role of the key. You can use `caver.wallet.keyring.role`.
* @param {number} [index] The index of the key to be used.
* @return {object}
*/
signMessage(message, role, index) {
if (role === undefined) throw new Error(`role should be defined for signMessage. Please use 'caver.wallet.keyring.role'.`)
const messageHash = utils.hashMessage(message)
const keys = this.getKeyByRole(role)
const signatures = []
if (index !== undefined) {
validateIndexWithKeys(index, keys.length)
signatures.push(keys[index].signMessage(messageHash))
} else {
for (const k of keys) {
signatures.push(k.signMessage(messageHash))
}
}
return {
messageHash,
signatures,
message,
}
}
/**
* returns keys by role. If the key of the role passed as parameter is empty, the default key is returned.
*
* @param {number} role A number indicating the role of the key. You can use `caver.wallet.keyring.role`.
* @return {Array.<PrivateKey>}
*/
getKeyByRole(role) {
if (role === undefined) throw new Error(`role should be defined.`)
if (role >= KEY_ROLE.roleLast || role < 0) throw new Error(`Invalid role number: ${role}`)
return this.keys
}
/**
* returns an instance of Account.
*
* @param {WeightedMultiSigOptions} [options] The options that includes 'threshold' and 'weight'. This is only necessary when keyring use multiple private keys.
* @return {Account}
*/
toAccount(options) {
if (_.isArray(options))
throw new Error(`For AccountKeyWeightedMultiSig, options cannot be defined as an array of WeightedMultiSigOptions.`)
options = fillWeightedMultiSigOptionsForMultiSig(this.keys.length, options)
const publicKeys = this.getPublicKey()
return Account.createWithAccountKeyWeightedMultiSig(this.address, publicKeys, options)
}
/**
* encrypts a keyring and returns a keystore v4 object.
*
* @param {string} password The password to be used for encryption. The encrypted key store can be decrypted with this password.
* @param {object} options The options to use when encrypt a keyring. Also address can be defined specifically in options object.
* @return {object}
*/
/**
* options can include below
* {
* salt: ...,
* iv: ...,
* kdf: ...,
* dklen: ...,
* c: ...,
* n: ...,
* r: ...,
* p: ...,
* cipher: ...,
* uuid: ...,
* cipher: ...,
* }
*/
encrypt(password, options = {}) {
let keyring = []
keyring = encryptKey(this.keys, password, options)
return formatEncrypted(4, this.address, keyring, options)
}
/**
* returns KlaytnWalletKey format. If keyring uses more than one private key, this function will throw error.
*
* @return {string}
*/
getKlaytnWalletKey() {
throw new Error(`Not supported for this class.`)
}
/**
* encrypts a keyring and returns a keystore v3 object.
*
* @param {string} password The password to be used for keyring encryption. The encrypted key store can be decrypted with this password.
* @param {object} options The options to use when encrypt a keyring. See `keyring.encrypt` for more detail about options.
* @return {object}
*/
encryptV3(password, options) {
throw new Error(`Not supported for this class. Use 'keyring.encrypt(password)'.`)
}
/**
* returns true if keyring has decoupled key.
*
* @return {boolean}
*/
isDecoupled() {
return true
}
}
module.exports = MultipleKeyring
/**
* Format the key parameters passed by the user to create a keyring instance.
* @param {Array.<string|PrivateKey>} keyInput The input parameter for key variable in Keyring.
* @return {Array.<PrivateKey>}
*/
function formattingForKeyInKeyring(keyInput) {
if (!_.isArray(keyInput)) {
throw new Error(`Invalid parameter. The private keys to add should be defined as an array.`)
}
const keys = []
for (let i = 0; i < keyInput.length; i++) {
keys.push(keyInput[i] instanceof PrivateKey ? keyInput[i] : new PrivateKey(keyInput[i]))
}
return keys
}